Changing error handling with scene file parsing

This commit is contained in:
Wito Wiala 2026-02-19 15:41:24 +00:00
parent 8a92d7642d
commit b36105edc1
22 changed files with 340 additions and 369 deletions

View file

@ -9,6 +9,8 @@ use_f64 = []
use_gpu = [] use_gpu = []
use_nvtx = [] use_nvtx = []
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"] cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"]
vulkan = ["ash", "gpu-allocator"]
ash = ["dep:ash"]
[dependencies] [dependencies]
anyhow = "1.0.100" anyhow = "1.0.100"
@ -33,12 +35,12 @@ wgpu = "27.0.1"
shared = { path = "shared" } shared = { path = "shared" }
ptex-filter = { path = "crates/ptex-filter" } ptex-filter = { path = "crates/ptex-filter" }
# kernels = { path = "kernels" } # 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 } 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 = { 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 } cust_raw = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
ptex = "0.3.0" ptex = "0.3.0"
# ptex-sys = "0.3.0"
slice = "0.0.4" slice = "0.0.4"
crossbeam-channel = "0.5.15" crossbeam-channel = "0.5.15"
num_cpus = "1.17.0" num_cpus = "1.17.0"

View file

@ -15,6 +15,4 @@ pub mod utils;
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
pub mod gpu; pub mod gpu;
pub use utils::arena::Arena; pub use utils::{Arena, FileLoc, ParameterDictionary};
pub use utils::error::FileLoc;
pub use utils::parameters::ParameterDictionary;

View file

@ -3,7 +3,7 @@ use crate::core::material::CreateMaterial;
use crate::core::texture::SpectrumTexture; use crate::core::texture::SpectrumTexture;
use crate::spectra::data::get_named_spectrum; use crate::spectra::data::get_named_spectrum;
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload, parameters::error_exit}; 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::material::Material;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType; use shared::core::texture::SpectrumType;
@ -27,32 +27,32 @@ impl CreateMaterial for CoatedDiffuseMaterial {
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))), SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))),
))); )));
let u_roughness = parameters let u_roughness =
.get_float_texture_or_null("uroughness") parameters.get_float_texture_with_fallback("uroughness", "roughness", 0.5)?;
.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 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 let eta = parameters
.get_float_array("eta") .get_float_array("eta")?
.first() .first()
.map(|&v| Spectrum::Constant(ConstantSpectrum::new(v))) .map(|&v| Spectrum::Constant(ConstantSpectrum::new(v)))
.or_else(|| parameters.get_one_spectrum("eta", None, SpectrumType::Unbounded)) .or_else(|| parameters.get_one_spectrum("eta", None, SpectrumType::Unbounded))
.unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5))); .unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5)));
let max_depth = parameters.get_one_int("maxdepth", 10); let max_depth = parameters.get_one_int("maxdepth", 10)?;
let n_samples = parameters.get_one_int("nsamples", 1); let n_samples = parameters.get_one_int("nsamples", 1)?;
let g = parameters.get_float_texture("g", 0.); let g = parameters.get_float_texture("g", 0.)?;
let albedo = parameters let albedo = parameters
.get_spectrum_texture("albedo", None, SpectrumType::Albedo) .get_spectrum_texture("albedo", None, SpectrumType::Albedo)
.unwrap_or_else(|| { .unwrap_or_else(|| {
let default_spectrum = Spectrum::Constant(ConstantSpectrum::new(0.)); let default_spectrum = Spectrum::Constant(ConstantSpectrum::new(0.));
SpectrumTexture::Constant(SpectrumConstantTexture::new(default_spectrum)).into() SpectrumTexture::Constant(SpectrumConstantTexture::new(default_spectrum)).into()
}); });
let displacement = parameters.get_float_texture("displacement", 0.); let displacement = parameters.get_float_texture("displacement", 0.)?;
let remap_roughness = parameters.get_one_bool("remaproughness", true); let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
let specific = CoatedDiffuseMaterial::new( let specific = CoatedDiffuseMaterial::new(
reflectance.upload(arena), reflectance.upload(arena),
u_roughness.upload(arena), u_roughness.upload(arena),
@ -80,68 +80,76 @@ impl CreateMaterial for CoatedConductorMaterial {
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Material> { ) -> Result<Material> {
let interface_u_roughness = parameters let interface_u_roughness = parameters.get_float_texture_with_fallback(
.get_float_texture_or_null("interface.uroughness") "interface.uroughness",
.unwrap_or_else(|| parameters.get_float_texture("interface.roughness", 0.)); "interface.roughness",
let interface_v_roughness = parameters 0.,
.get_float_texture_or_null("interface.vroughness") )?;
.unwrap_or_else(|| parameters.get_float_texture("interface.vroughness", 0.)); let interface_v_roughness = parameters.get_float_texture_with_fallback(
let thickness = parameters.get_float_texture("interface.thickness", 0.01); "interface.vroughness",
"interface.roughness",
0.,
)?;
let thickness = parameters.get_float_texture("interface.thickness", 0.01)?;
let interface_eta: Spectrum = parameters let interface_eta: Spectrum = parameters
.get_float_array("interface.eta") .get_float_array("interface.eta")?
.first() .first()
.map(|&v| Spectrum::Constant(ConstantSpectrum::new(v))) .map(|&v| Spectrum::Constant(ConstantSpectrum::new(v)))
.or_else(|| parameters.get_one_spectrum("interface.eta", None, SpectrumType::Unbounded)) .or_else(|| parameters.get_one_spectrum("interface.eta", None, SpectrumType::Unbounded))
.unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5))); .unwrap_or_else(|| Spectrum::Constant(ConstantSpectrum::new(1.5)));
let conductor_u_roughness = parameters
.get_float_texture_or_null("conductor.uroughness") let conductor_u_roughness = parameters.get_float_texture_with_fallback(
.unwrap_or_else(|| parameters.get_float_texture("conductor.roughness", 0.)); "conductor.uroughness",
let conductor_v_roughness = parameters "conductor.roughness",
.get_float_texture_or_null("conductor.vroughness") 0.,
.unwrap_or_else(|| parameters.get_float_texture("conductor.vroughness", 0.)); )?;
let conductor_v_roughness = parameters.get_float_texture_with_fallback(
"conductor.vroughness",
"conductor.roughness",
0.,
)?;
let reflectance = let reflectance =
parameters.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo); parameters.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo);
let conductor_eta = let conductor_eta =
parameters.get_spectrum_texture_or_null("conductor.eta", SpectrumType::Unbounded); parameters.get_spectrum_texture_or_null("conductor.eta", SpectrumType::Unbounded);
let k = parameters.get_spectrum_texture_or_null("conductor.k", SpectrumType::Unbounded); let k = parameters.get_spectrum_texture_or_null("conductor.k", SpectrumType::Unbounded);
let (conductor_eta, k) = match (&reflectance, conductor_eta, k) { let (conductor_eta, k) = if reflectance.is_some() {
(Some(_), Some(_), _) | (Some(_), _, Some(_)) => { if conductor_eta.is_some() || k.is_some() {
return Err(error_exit( bail!(
Some(loc), "{}: For the coated conductor material, both \"reflectance\" \
"For the coated conductor material, both \"reflectance\" \ and \"eta\"/\"k\" cannot be provided simultaneously.",
and \"eta\" and \"k\" can't be provided.", loc
)); );
} }
(None, None)
(None, eta, k) => { } else {
let final_eta = eta.unwrap_or_else(|| { let eta = conductor_eta.unwrap_or_else(|| {
let s = get_named_spectrum("metal-Cu-eta").expect("Missing copper spectrum"); let s = get_named_spectrum("metal-Cu-eta").expect("Missing copper spectrum");
Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s)))
}); });
let k = k.unwrap_or_else(|| {
let final_k = k.unwrap_or_else(|| {
let s = get_named_spectrum("metal-Cu-k").expect("Missing copper spectrum"); let s = get_named_spectrum("metal-Cu-k").expect("Missing copper spectrum");
Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s)))
}); });
(Some(eta), Some(k))
(Some(final_eta), Some(final_k))
}
(Some(_), None, None) => (None, None),
}; };
let max_depth = parameters.get_one_int("maxdepth", 10); let max_depth = parameters.get_one_int("maxdepth", 10)?;
let n_samples = parameters.get_one_int("nsamples", 1); let n_samples = parameters.get_one_int("nsamples", 1)?;
let g = parameters.get_float_texture("g", 0.); let g = parameters.get_float_texture("g", 0.)?;
let albedo = parameters let albedo = parameters
.get_spectrum_texture("albedo", None, SpectrumType::Albedo) .get_spectrum_texture("albedo", None, SpectrumType::Albedo)
.unwrap_or_else(|| { .unwrap_or_else(|| {
let spectrum = Spectrum::Constant(ConstantSpectrum::new(0.)); let spectrum = Spectrum::Constant(ConstantSpectrum::new(0.));
SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum)).into() SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum)).into()
}); });
let displacement = parameters.get_float_texture_or_null("displacement"); let displacement = parameters.get_float_texture_or_null("displacement")?;
let remap_roughness = parameters.get_one_bool("remaproughness", true); let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
let material = Self::new( let material = Self::new(
normal_map.upload(arena), normal_map.upload(arena),
displacement.upload(arena), displacement.upload(arena),

View file

@ -27,8 +27,8 @@ impl CreateMaterial for HairMaterial {
let reflectance = parameters let reflectance = parameters
.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo) .get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo)
.or_else(|| parameters.get_spectrum_texture_or_null("color", SpectrumType::Albedo)); .or_else(|| parameters.get_spectrum_texture_or_null("color", SpectrumType::Albedo));
let eumelanin = parameters.get_float_texture_or_null("eumelanin"); let eumelanin = parameters.get_float_texture_or_null("eumelanin")?;
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin"); let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
let has_melanin = eumelanin.is_some() || pheomelanin.is_some(); let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
// Default distribution if nothing is spceified // Default distribution if nothing is spceified
@ -42,10 +42,10 @@ impl CreateMaterial for HairMaterial {
sigma_a sigma_a
}; };
let eta = parameters.get_float_texture("eta", 1.55); let eta = parameters.get_float_texture("eta", 1.55)?;
let beta_m = parameters.get_float_texture("beta_m", 0.3); let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
let beta_n = parameters.get_float_texture("beta_n", 0.3); let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
let alpha = parameters.get_float_texture("alpha", 2.); let alpha = parameters.get_float_texture("alpha", 2.)?;
let material = HairMaterial::new( let material = HairMaterial::new(
sigma_a.upload(arena), sigma_a.upload(arena),
reflectance.upload(arena), reflectance.upload(arena),

View file

@ -71,14 +71,15 @@ impl CreateSampler for HaltonSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = options let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
.quick_render n
.then_some(1) } else {
.or(options.pixel_samples) params.get_one_int("pixelsamples", 16)?
.unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); };
let seed = params.get_one_int("seed", options.seed);
let seed = params.get_one_int("seed", options.seed)?;
let s = match params let s = match params
.get_one_string("randomization", "permutedigits") .get_one_string("randomization", "permutedigits")?
.as_str() .as_str()
{ {
"none" => RandomizeStrategy::None, "none" => RandomizeStrategy::None,

View file

@ -11,12 +11,14 @@ impl CreateSampler for IndependentSampler {
arena: &Arena, arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = options
.quick_render let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
.then_some(1) n
.or(options.pixel_samples) } else {
.unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); params.get_one_int("pixelsamples", 16)?
let seed = params.get_one_int("seed", options.seed); };
let seed = params.get_one_int("seed", options.seed)?;
let sampler = Self::new(nsamp, seed as u64); let sampler = Self::new(nsamp, seed as u64);
arena.alloc(sampler); arena.alloc(sampler);
Ok(Sampler::Independent(sampler)) Ok(Sampler::Independent(sampler))

View file

@ -12,13 +12,15 @@ impl CreateSampler for SobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = options let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
.quick_render n
.then_some(1) } else {
.or(options.pixel_samples) params.get_one_int("pixelsamples", 16)?
.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 seed = params.get_one_int("seed", options.seed)?;
let s = match params.get_one_string("randomization", "fastowen")?.as_str() {
"none" => RandomizeStrategy::None, "none" => RandomizeStrategy::None,
"permutedigits" => RandomizeStrategy::PermuteDigits, "permutedigits" => RandomizeStrategy::PermuteDigits,
"fastowen" => RandomizeStrategy::FastOwen, "fastowen" => RandomizeStrategy::FastOwen,
@ -42,13 +44,13 @@ impl CreateSampler for PaddedSobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = options let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
.quick_render n
.then_some(1) } else {
.or(options.pixel_samples) params.get_one_int("pixelsamples", 16)?
.unwrap_or_else(|| params.get_one_int("pixelsamples", 16)); };
let seed = params.get_one_int("seed", options.seed); let seed = params.get_one_int("seed", options.seed)?;
let s = match params.get_one_string("randomization", "fastowen").as_str() { let s = match params.get_one_string("randomization", "fastowen")?.as_str() {
"none" => RandomizeStrategy::None, "none" => RandomizeStrategy::None,
"permutedigits" => RandomizeStrategy::PermuteDigits, "permutedigits" => RandomizeStrategy::PermuteDigits,
"fastowen" => RandomizeStrategy::FastOwen, "fastowen" => RandomizeStrategy::FastOwen,
@ -75,13 +77,14 @@ impl CreateSampler for ZSobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = options let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) {
.quick_render n
.then_some(1) } else {
.or(options.pixel_samples) params.get_one_int("pixelsamples", 16)?
.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 seed = params.get_one_int("seed", options.seed)?;
let s = match params.get_one_string("randomization", "fastowen")?.as_str() {
"none" => RandomizeStrategy::None, "none" => RandomizeStrategy::None,
"permutedigits" => RandomizeStrategy::PermuteDigits, "permutedigits" => RandomizeStrategy::PermuteDigits,
"fastowen" => RandomizeStrategy::FastOwen, "fastowen" => RandomizeStrategy::FastOwen,

View file

@ -11,7 +11,7 @@ impl CreateSampler for StratifiedSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); 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 { let (x_samples, y_samples) = if options.quick_render {
(1, 1) (1, 1)
} else if let Some(n) = options.pixel_samples { } else if let Some(n) = options.pixel_samples {
@ -21,11 +21,11 @@ impl CreateSampler for StratifiedSampler {
(n / y, y) (n / y, y)
} else { } else {
( (
params.get_one_int("xsamples", 4), params.get_one_int("xsamples", 4)?,
params.get_one_int("ysamples", 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); let sampler = Self::new(x_samples, y_samples, Some(seed as u64), jitter);
// arena.aloc(sampler); // arena.aloc(sampler);

View file

@ -23,11 +23,11 @@ impl CreateShape for BilinearPatchShape {
_loc: FileLoc, _loc: FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let mut vertex_indices = parameters.get_int_array("indices"); let mut vertex_indices = parameters.get_int_array("indices")?;
let p = parameters.get_point3f_array("P"); let p = parameters.get_point3f_array("P")?;
let mut uv = parameters.get_point2f_array("uv"); let mut uv = parameters.get_point2f_array("uv")?;
let mut n = parameters.get_normal3f_array("N"); let mut n = parameters.get_normal3f_array("N")?;
let mut face_indices = parameters.get_int_array("faceIndices"); let mut face_indices = parameters.get_int_array("faceIndices")?;
if vertex_indices.is_empty() { if vertex_indices.is_empty() {
if p.len() == 4 { if p.len() == 4 {
@ -84,7 +84,7 @@ impl CreateShape for BilinearPatchShape {
face_indices.clear(); face_indices.clear();
} }
let filename = parameters.get_one_string("emissionfilename", ""); let filename = parameters.get_one_string("emissionfilename", "")?;
let image_dist = if !filename.is_empty() { let image_dist = if !filename.is_empty() {
if !uv.is_empty() { if !uv.is_empty() {

View file

@ -66,11 +66,11 @@ impl CreateShape for CurveShape {
_loc: FileLoc, _loc: FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let width = parameters.get_one_float("width", 1.0); let width = parameters.get_one_float("width", 1.0)?;
let width0 = parameters.get_one_float("width0", width); let width0 = parameters.get_one_float("width0", width)?;
let width1 = parameters.get_one_float("width1", 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 { if degree != 2 && degree != 3 {
return Err(anyhow!( return Err(anyhow!(
"Invalid degree {}: only degree 2 and 3 curves are supported.", "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" { if basis != "bezier" && basis != "bspline" {
return Err(anyhow!( return Err(anyhow!(
"Invalid basis \"{}\": only \"bezier\" and \"bspline\" are supported.", "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; let n_segments;
if basis == "bezier" { if basis == "bezier" {
@ -114,7 +114,7 @@ impl CreateShape for CurveShape {
n_segments = cp.len() - degree as usize; 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() { let curve_type = match curve_type_str.as_str() {
"flat" => CurveType::Flat, "flat" => CurveType::Flat,
"ribbon" => CurveType::Ribbon, "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 !n.is_empty() {
if curve_type != CurveType::Ribbon { if curve_type != CurveType::Ribbon {
warn!("Curve normals are only used with \"ribbon\" type curves. Discarding."); 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 { let split_depth = if use_gpu {
0 0
} else { } else {
parameters.get_one_int("splitdepth", 3) parameters.get_one_int("splitdepth", 3)?
}; };
let mut curves: Vec<Shape> = Vec::new(); let mut curves: Vec<Shape> = Vec::new();

View file

@ -18,10 +18,10 @@ impl CreateShape for CylinderShape {
_loc: FileLoc, _loc: FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let radius = parameters.get_one_float("radius", 1.); let radius = parameters.get_one_float("radius", 1.)?;
let z_min = parameters.get_one_float("zmin", -1.); let z_min = parameters.get_one_float("zmin", -1.)?;
let z_max = parameters.get_one_float("zmax", 1.); let z_max = parameters.get_one_float("zmax", 1.)?;
let phi_max = parameters.get_one_float("phimax", 360.); let phi_max = parameters.get_one_float("phimax", 360.)?;
let shape = CylinderShape::new( let shape = CylinderShape::new(
render_from_object, render_from_object,
object_from_render, object_from_render,

View file

@ -18,10 +18,10 @@ impl CreateShape for DiskShape {
_loc: FileLoc, _loc: FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let height = parameters.get_one_float("height", 0.); let height = parameters.get_one_float("height", 0.)?;
let radius = parameters.get_one_float("radius", 1.); let radius = parameters.get_one_float("radius", 1.)?;
let inner_radius = parameters.get_one_float("innerradius", 0.); let inner_radius = parameters.get_one_float("innerradius", 0.)?;
let phi_max = parameters.get_one_float("phimax", 360.); let phi_max = parameters.get_one_float("phimax", 360.)?;
let shape = DiskShape::new( let shape = DiskShape::new(
radius, radius,
inner_radius, inner_radius,

View file

@ -246,9 +246,7 @@ impl BilinearPatchMesh {
} }
} }
// ============================================================================
// PLY Helper Functions // PLY Helper Functions
// ============================================================================
fn get_float(elem: &DefaultElement, key: &str) -> AnyResult<f32> { fn get_float(elem: &DefaultElement, key: &str) -> AnyResult<f32> {
match elem.get(key) { match elem.get(key) {
@ -290,9 +288,7 @@ fn get_list_uint(elem: &DefaultElement, key: &str) -> AnyResult<Vec<u32>> {
} }
} }
// ============================================================================
// TriQuadMesh Implementation // TriQuadMesh Implementation
// ============================================================================
impl TriQuadMesh { impl TriQuadMesh {
pub fn read_ply<P: AsRef<Path>>(filename: P) -> AnyResult<Self> { pub fn read_ply<P: AsRef<Path>>(filename: P) -> AnyResult<Self> {
@ -510,9 +506,7 @@ impl TriQuadMesh {
} }
} }
// ============================================================================ // Create from PLY directly
// Convenience: Create from PLY directly
// ============================================================================
impl TriangleMesh { impl TriangleMesh {
pub fn from_ply<P: AsRef<Path>>( pub fn from_ply<P: AsRef<Path>>(

View file

@ -18,10 +18,10 @@ impl CreateShape for SphereShape {
_loc: FileLoc, _loc: FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let radius = parameters.get_one_float("radius", 1.); let radius = parameters.get_one_float("radius", 1.)?;
let zmin = parameters.get_one_float("zmin", -radius); let zmin = parameters.get_one_float("zmin", -radius)?;
let zmax = parameters.get_one_float("zmax", radius); let zmax = parameters.get_one_float("zmax", radius)?;
let phimax = parameters.get_one_float("phimax", 360.); let phimax = parameters.get_one_float("phimax", 360.)?;
let shape = SphereShape::new( let shape = SphereShape::new(
render_from_object, render_from_object,
object_from_render, object_from_render,

View file

@ -20,11 +20,11 @@ impl CreateShape for TriangleShape {
_loc: FileLoc, _loc: FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<Vec<Shape>> { ) -> Result<Vec<Shape>> {
let mut vertex_indices = parameters.get_int_array("indices"); let mut vertex_indices = parameters.get_int_array("indices")?;
let p = parameters.get_point3f_array("P"); let p = parameters.get_point3f_array("P")?;
let mut uvs = parameters.get_point2f_array("uv"); let mut uvs = parameters.get_point2f_array("uv")?;
let mut s = parameters.get_vector3f_array("S"); let mut s = parameters.get_vector3f_array("S")?;
let mut n = parameters.get_normal3f_array("N"); let mut n = parameters.get_normal3f_array("N")?;
if vertex_indices.is_empty() { if vertex_indices.is_empty() {
if p.len() == 3 { 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; let n_triangles = vertex_indices.len() / 3;
if !face_indices.is_empty() && face_indices.len() != n_triangles { if !face_indices.is_empty() && face_indices.len() != n_triangles {

View file

@ -33,9 +33,9 @@ impl FloatMixTexture {
_loc: &FileLoc, _loc: &FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<FloatTexture> { ) -> Result<FloatTexture> {
let tex1 = params.get_float_texture("tex1", 0.); let tex1 = params.get_float_texture("tex1", 0.)?;
let tex2 = params.get_float_texture("tex2", 1.); let tex2 = params.get_float_texture("tex2", 1.)?;
let amount = params.get_float_texture("amount", 0.5); let amount = params.get_float_texture("amount", 0.5)?;
// arena.alloc(Self::new(tex1, tex2, amount)); // arena.alloc(Self::new(tex1, tex2, amount));
Ok(FloatTexture::Mix(Self::new(tex1, tex2, amount))) Ok(FloatTexture::Mix(Self::new(tex1, tex2, amount)))
} }
@ -74,10 +74,10 @@ impl FloatDirectionMixTexture {
_loc: &FileLoc, _loc: &FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<FloatTexture> { ) -> Result<FloatTexture> {
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 dir = render_from_texture.apply_to_vector(dir_raw).normalize();
let tex1 = params.get_float_texture("tex1", 0.); let tex1 = params.get_float_texture("tex1", 0.)?;
let tex2 = params.get_float_texture("tex2", 1.); let tex2 = params.get_float_texture("tex2", 1.)?;
// arena.alloc(Self::new(tex1, tex2, dir)) // arena.alloc(Self::new(tex1, tex2, dir))
Ok(FloatTexture::DirectionMix(Self::new(tex1, tex2, dir))) Ok(FloatTexture::DirectionMix(Self::new(tex1, tex2, dir)))
} }

View file

@ -26,8 +26,8 @@ impl FloatScaledTexture {
_loc: &FileLoc, _loc: &FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<FloatTexture> { ) -> Result<FloatTexture> {
let mut tex = params.get_float_texture("tex", 1.); let mut tex = params.get_float_texture("tex", 1.)?;
let mut scale = params.get_float_texture("scale", 1.); let mut scale = params.get_float_texture("scale", 1.)?;
for _ in 0..2 { for _ in 0..2 {
if let FloatTexture::Constant(c_tex) = &*scale { if let FloatTexture::Constant(c_tex) = &*scale {

View file

@ -2,6 +2,7 @@ use crate::core::image::Image;
use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::shapes::{BilinearPatchMesh, TriangleMesh}; use crate::shapes::{BilinearPatchMesh, TriangleMesh};
use crate::spectra::DenselySampledSpectrumBuffer; use crate::spectra::DenselySampledSpectrumBuffer;
use crate::utils::backend::GpuAllocator;
use crate::utils::mipmap::MIPMap; use crate::utils::mipmap::MIPMap;
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D}; use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -24,7 +25,8 @@ use std::collections::HashMap;
use std::slice::from_raw_parts; use std::slice::from_raw_parts;
use std::sync::Arc; use std::sync::Arc;
pub struct Arena { pub struct Arena<A: GpuAllocator> {
allocator: A,
inner: Mutex<ArenaInner>, inner: Mutex<ArenaInner>,
} }
@ -33,9 +35,10 @@ struct ArenaInner {
texture_cache: HashMap<usize, u64>, texture_cache: HashMap<usize, u64>,
} }
impl Arena { impl<A: GpuAllocator> Arena<A> {
pub fn new() -> Self { pub fn new(allocator: A) -> Self {
Self { Self {
allocator,
inner: Mutex::new(ArenaInner { inner: Mutex::new(ArenaInner {
buffer: Vec::new(), buffer: Vec::new(),
texture_cache: HashMap::new(), texture_cache: HashMap::new(),
@ -45,12 +48,10 @@ impl Arena {
pub fn alloc<T>(&self, value: T) -> Ptr<T> { pub fn alloc<T>(&self, value: T) -> Ptr<T> {
let layout = Layout::new::<T>(); let layout = Layout::new::<T>();
let ptr = unsafe { self.alloc_unified(layout) } as *mut T; let ptr = unsafe { self.allocator.alloc(layout) } as *mut T;
unsafe { ptr.write(value) };
unsafe {
ptr.write(value);
}
self.inner.lock().buffer.push((ptr as *mut u8, layout));
Ptr::from_raw(ptr) Ptr::from_raw(ptr)
} }
@ -67,44 +68,13 @@ impl Arena {
} }
let layout = Layout::array::<T>(values.len()).unwrap(); let layout = Layout::array::<T>(values.len()).unwrap();
let ptr = unsafe { self.alloc_unified(layout) } as *mut T; let ptr = unsafe { self.allocator.alloc(layout) } as *mut T;
unsafe { std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len()) };
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()) (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::<u8>(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<MIPMap>) -> u64 { pub fn get_texture_object(&self, mipmap: &Arc<MIPMap>) -> u64 {
let key = Arc::as_ptr(mipmap) as usize; let key = Arc::as_ptr(mipmap) as usize;
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
@ -113,107 +83,85 @@ impl Arena {
return tex_obj; return tex_obj;
} }
#[cfg(feature = "cuda")] // TODO: Backend-specific texture object creation.
let tex_obj = self.create_cuda_texture(mipmap); // CUDA: cudaCreateTextureObject
// Vulkan: VkImageView + VkSampler -> descriptor index
#[cfg(not(feature = "cuda"))]
let tex_obj = 0u64; let tex_obj = 0u64;
inner.texture_cache.insert(key, tex_obj); inner.texture_cache.insert(key, tex_obj);
tex_obj tex_obj
} }
}
#[cfg(feature = "cuda")] impl<A: GpuAllocator + Default> Default for Arena<A> {
fn create_cuda_texture(&self, mipmap: &MIPMap) -> u64 { fn default() -> Self {
// TODO: Implement with cudarc Self::new(A::default())
// 1. Get image data from mipmap.base_image()
// 2. cudaMallocArray
// 3. cudaMemcpy2DToArray
// 4. cudaCreateTextureObject
// 5. Return handle
0
} }
} }
impl Drop for Arena { impl<A: GpuAllocator> Drop for Arena<A> {
fn drop(&mut self) { fn drop(&mut self) {
let inner = self.inner.get_mut(); let inner = self.inner.get_mut();
for (ptr, layout) in inner.buffer.drain(..) { for (ptr, layout) in inner.buffer.drain(..) {
if layout.size() == 0 { unsafe { self.allocator.dealloc(ptr, layout) };
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 impl Send for Arena {} unsafe impl<A: GpuAllocator> Send for Arena<A> {}
unsafe impl Sync for Arena {} unsafe impl<A: GpuAllocator> Sync for Arena<A> {}
pub trait Upload { pub trait Upload {
type Target: Copy; type Target: Copy;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target>; fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target>;
} }
impl Upload for Shape { impl Upload for Shape {
type Target = Shape; type Target = Shape;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(self.clone()) arena.alloc(self.clone())
} }
} }
impl Upload for Light { impl Upload for Light {
type Target = Light; type Target = Light;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(self.clone()) arena.alloc(self.clone())
} }
} }
impl Upload for Image { impl Upload for Image {
type Target = DeviceImage; type Target = DeviceImage;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(*self.device()) arena.alloc(*self.device())
} }
} }
impl Upload for Spectrum { impl Upload for Spectrum {
type Target = Spectrum; type Target = Spectrum;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(self.clone()) arena.alloc(self.clone())
} }
} }
impl Upload for Material { impl Upload for Material {
type Target = Material; type Target = Material;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(self.clone()) arena.alloc(self.clone())
} }
} }
impl Upload for DenselySampledSpectrumBuffer { impl Upload for DenselySampledSpectrumBuffer {
type Target = DenselySampledSpectrum; type Target = DenselySampledSpectrum;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
arena.alloc(*&self.device()) arena.alloc(*&self.device())
} }
} }
impl Upload for SpectrumTexture { impl Upload for SpectrumTexture {
type Target = GPUSpectrumTexture; type Target = GPUSpectrumTexture;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let gpu_variant = match self { let gpu_variant = match self {
SpectrumTexture::Constant(tex) => GPUSpectrumTexture::Constant(tex.clone()), SpectrumTexture::Constant(tex) => GPUSpectrumTexture::Constant(tex.clone()),
SpectrumTexture::Checkerboard(tex) => GPUSpectrumTexture::Checkerboard(tex.clone()), SpectrumTexture::Checkerboard(tex) => GPUSpectrumTexture::Checkerboard(tex.clone()),
@ -241,15 +189,6 @@ impl Upload for SpectrumTexture {
}; };
GPUSpectrumTexture::Scaled(gpu_scaled) 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::Marble(tex) => GPUSpectrumTexture::Marble(tex.clone()),
SpectrumTexture::Mix(tex) => { SpectrumTexture::Mix(tex) => {
let tex1_ptr = tex.tex1.upload(arena); let tex1_ptr = tex.tex1.upload(arena);
@ -284,7 +223,7 @@ impl Upload for SpectrumTexture {
impl Upload for FloatTexture { impl Upload for FloatTexture {
type Target = GPUFloatTexture; type Target = GPUFloatTexture;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let gpu_variant = match self { let gpu_variant = match self {
FloatTexture::Constant(tex) => GPUFloatTexture::Constant(tex.clone()), FloatTexture::Constant(tex) => GPUFloatTexture::Constant(tex.clone()),
FloatTexture::Checkerboard(tex) => GPUFloatTexture::Checkerboard(tex.clone()), FloatTexture::Checkerboard(tex) => GPUFloatTexture::Checkerboard(tex.clone()),
@ -346,7 +285,7 @@ impl Upload for FloatTexture {
impl Upload for RGBToSpectrumTable { impl Upload for RGBToSpectrumTable {
type Target = RGBToSpectrumTable; type Target = RGBToSpectrumTable;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let n_nodes = self.n_nodes as usize; let n_nodes = self.n_nodes as usize;
let z_slice = unsafe { from_raw_parts(self.z_nodes.as_raw(), n_nodes) }; 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) }; 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 { impl Upload for RGBColorSpace {
type Target = RGBColorSpace; type Target = RGBColorSpace;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let table_ptr = self.rgb_to_spectrum_table.upload(arena); let table_ptr = self.rgb_to_spectrum_table.upload(arena);
let shared_space = RGBColorSpace { let shared_space = RGBColorSpace {
@ -387,7 +326,7 @@ impl Upload for RGBColorSpace {
impl Upload for DeviceStandardColorSpaces { impl Upload for DeviceStandardColorSpaces {
type Target = DeviceStandardColorSpaces; type Target = DeviceStandardColorSpaces;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let srgb_ptr = self.srgb.upload(arena); let srgb_ptr = self.srgb.upload(arena);
let dci_ptr = self.dci_p3.upload(arena); let dci_ptr = self.dci_p3.upload(arena);
let rec_ptr = self.rec2020.upload(arena); let rec_ptr = self.rec2020.upload(arena);
@ -407,7 +346,7 @@ impl Upload for DeviceStandardColorSpaces {
impl Upload for PiecewiseConstant2D { impl Upload for PiecewiseConstant2D {
type Target = DevicePiecewiseConstant2D; type Target = DevicePiecewiseConstant2D;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let marginal_shared = self.marginal.to_shared(arena); let marginal_shared = self.marginal.to_shared(arena);
let conditionals_shared: Vec<DevicePiecewiseConstant1D> = self let conditionals_shared: Vec<DevicePiecewiseConstant1D> = self
@ -430,7 +369,7 @@ impl Upload for PiecewiseConstant2D {
impl Upload for WindowedPiecewiseConstant2D { impl Upload for WindowedPiecewiseConstant2D {
type Target = DeviceWindowedPiecewiseConstant2D; type Target = DeviceWindowedPiecewiseConstant2D;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let specific = DeviceWindowedPiecewiseConstant2D { let specific = DeviceWindowedPiecewiseConstant2D {
sat: self.sat, sat: self.sat,
func: self.func, func: self.func,
@ -442,7 +381,7 @@ impl Upload for WindowedPiecewiseConstant2D {
impl Upload for TriangleMesh { impl Upload for TriangleMesh {
type Target = DeviceTriangleMesh; type Target = DeviceTriangleMesh;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let storage = &self.storage; let storage = &self.storage;
// Upload all arrays to arena // Upload all arrays to arena
@ -490,7 +429,7 @@ impl Upload for TriangleMesh {
impl Upload for BilinearPatchMesh { impl Upload for BilinearPatchMesh {
type Target = DeviceBilinearPatchMesh; type Target = DeviceBilinearPatchMesh;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let storage = &self.storage; let storage = &self.storage;
let (vertex_indices_ptr, _) = arena.alloc_slice(&storage.vertex_indices); let (vertex_indices_ptr, _) = arena.alloc_slice(&storage.vertex_indices);
@ -525,7 +464,7 @@ impl Upload for BilinearPatchMesh {
impl<T: Upload> Upload for Option<T> { impl<T: Upload> Upload for Option<T> {
type Target = T::Target; type Target = T::Target;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
match self { match self {
Some(val) => val.upload(arena), Some(val) => val.upload(arena),
None => Ptr::null(), None => Ptr::null(),
@ -536,7 +475,7 @@ impl<T: Upload> Upload for Option<T> {
impl<T: Upload> Upload for Arc<T> { impl<T: Upload> Upload for Arc<T> {
type Target = T::Target; type Target = T::Target;
fn upload(&self, arena: &Arena) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
(**self).upload(arena) (**self).upload(arena)
} }
} }

View file

@ -1,4 +1,5 @@
pub mod arena; pub mod arena;
pub mod backend;
pub mod containers; pub mod containers;
pub mod error; pub mod error;
pub mod file; pub mod file;
@ -11,10 +12,19 @@ pub mod parser;
pub mod sampling; pub mod sampling;
pub mod strings; pub mod strings;
pub use arena::{Arena, Upload}; pub use arena::Upload;
pub use error::FileLoc; pub use error::FileLoc;
pub use file::{read_float_file, resolve_filename}; pub use file::{read_float_file, resolve_filename};
pub use parameters::{ pub use parameters::{
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary, ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
}; };
pub use strings::*; pub use strings::*;
#[cfg(feature = "vulkan")]
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
#[cfg(all(feature = "cuda", not(feature = "vulkan")))]
pub type Arena = arena::Arena<backend::CudaAllocator>;
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
pub type Arena = arena::Arena<backend::SystemAllocator>;

View file

@ -3,6 +3,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::spectra::data::get_named_spectrum; use crate::spectra::data::get_named_spectrum;
use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer; use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer;
use crate::utils::FileLoc; use crate::utils::FileLoc;
use anyhow::{Result, bail};
use shared::Float; use shared::Float;
use shared::core::color::RGB; use shared::core::color::RGB;
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f}; use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f};
@ -19,14 +20,12 @@ use std::sync::{
atomic::{AtomicBool, Ordering}, 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 { if let Some(l) = loc {
log::error!("Error: {}: {}", l, message); format!("{}: {}", l, message)
} else { } else {
log::error!("Error: {}", message); message.to_string()
} }
std::process::exit(1);
} }
#[derive(Debug)] #[derive(Debug)]
@ -250,7 +249,10 @@ pub struct ParameterDictionary {
} }
impl ParameterDictionary { impl ParameterDictionary {
pub fn new(mut params: ParsedParameterVector, color_space: Option<Arc<RGBColorSpace>>) -> Self { pub fn new(
mut params: ParsedParameterVector,
color_space: Option<Arc<RGBColorSpace>>,
) -> Result<Self> {
let n_owned_params = params.len(); let n_owned_params = params.len();
params.reverse(); params.reverse();
let dict = Self { let dict = Self {
@ -258,15 +260,15 @@ impl ParameterDictionary {
color_space, color_space,
n_owned_params, n_owned_params,
}; };
dict.check_parameter_types(); dict.check_parameter_types()?;
dict Ok(dict)
} }
pub fn from_array( pub fn from_array(
mut p0: ParsedParameterVector, mut p0: ParsedParameterVector,
params: &[ParsedParameter], params: &[ParsedParameter],
color_space: Option<Arc<RGBColorSpace>>, color_space: Option<Arc<RGBColorSpace>>,
) -> Self { ) -> Result<Self> {
let n_owned_params = params.len(); let n_owned_params = params.len();
p0.extend(params.iter().rev().cloned()); p0.extend(params.iter().rev().cloned());
@ -275,21 +277,19 @@ impl ParameterDictionary {
color_space, color_space,
n_owned_params, n_owned_params,
}; };
dict.check_parameter_types(); dict.check_parameter_types()?;
dict Ok(dict)
} }
fn check_parameter_types(&self) { fn check_parameter_types(&self) -> Result<()> {
for p in &self.params { for p in &self.params {
match p.type_name.as_str() { match p.type_name.as_str() {
bool::TYPE_NAME => { bool::TYPE_NAME => {
if p.bools.is_empty() { if p.bools.is_empty() {
error_exit( bail!(
Some(&p.loc), "{}: non-Boolean values provided for Boolean-valued parameter \"{}\"",
&format!( &p.loc,
"\"{}\": non-Boolean values provided for Boolean-valued parameter",
p.name p.name
),
); );
} }
} }
@ -304,51 +304,49 @@ impl ParameterDictionary {
| "rgb" | "rgb"
| "blackbody" => { | "blackbody" => {
if p.ints.is_empty() && p.floats.is_empty() { if p.ints.is_empty() && p.floats.is_empty() {
error_exit( bail!(
Some(&p.loc), "{}: non-numeric values provided for numeric-valued parameter \"{}\"",
&format!( &p.loc,
"\"{}\": non-numeric values provided for numeric-valued parameter",
p.name p.name
),
); );
} }
} }
String::TYPE_NAME | "texture" => { String::TYPE_NAME | "texture" => {
if p.strings.is_empty() { if p.strings.is_empty() {
error_exit( bail!(
Some(&p.loc), "{}: non-string values provided for string-valued parameter \"{}\"",
&format!( &p.loc,
"\"{}\": non-string values provided for string-valued parameter",
p.name p.name
),
); );
} }
} }
"spectrum" => { "spectrum" => {
if p.strings.is_empty() && p.ints.is_empty() && p.floats.is_empty() { if p.strings.is_empty() && p.ints.is_empty() && p.floats.is_empty() {
error_exit( bail!(
Some(&p.loc), "{}: expecting string or numeric-valued parameter for spectrum parameter \"{}\"",
&format!( &p.loc,
"\"{}\": expecting string or numeric-valued parameter for spectrum parameter",
p.name p.name
),
); );
} }
} }
unknown => { unknown => {
error_exit( bail!(
Some(&p.loc), "{}: unknown parameter type \"{}\" '{}'",
&format!("\"{}\": unknown parameter type '{}'", p.name, unknown), &p.loc,
p.name,
unknown,
); );
} }
} }
} }
Ok(())
} }
fn lookup_single<T>(&self, name: &str, default_val: T) -> T fn lookup_single<T>(&self, name: &str, default_val: T) -> Result<T>
where where
T: PBRTParameter, T: PBRTParameter,
{ {
@ -357,32 +355,31 @@ impl ParameterDictionary {
let values = T::get_values(&param); let values = T::get_values(&param);
if values.is_empty() { if values.is_empty() {
error_exit( bail!(
Some(&param.loc), "{}: No values provided for parameter \"{}\".",
&format!("No values provided for parameter \"{}\".", name), &param.loc,
name
); );
} }
if values.len() != T::N_PER_ITEM { if values.len() != T::N_PER_ITEM {
error_exit( bail!(
Some(&param.loc), "{}: Expected {} values for parameter \"{}\". Found {}.",
&format!( &param.loc,
"Expected {} values for parameter \"{}\". Found {}.",
T::N_PER_ITEM, T::N_PER_ITEM,
name, name,
values.len() values.len()
),
); );
} }
param.looked_up.store(true, Ordering::Relaxed); 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<T>(&self, name: &str) -> Vec<T> pub fn lookup_array<T>(&self, name: &str) -> Result<Vec<T>>
where where
T: PBRTParameter, T: PBRTParameter,
{ {
@ -391,92 +388,90 @@ impl ParameterDictionary {
let values = T::get_values(param); let values = T::get_values(param);
if values.len() % T::N_PER_ITEM != 0 { if values.len() % T::N_PER_ITEM != 0 {
error_exit( bail!(
Some(&param.loc), "{}: Number of values for \"{}\" is not a multiple of {}",
&format!( &param.loc,
"Number of values for \"{}\" is not a multiple of {}",
name, name,
T::N_PER_ITEM T::N_PER_ITEM
),
); );
} }
param.looked_up.store(true, Ordering::Relaxed); param.looked_up.store(true, Ordering::Relaxed);
return values return Ok(values
.chunks(T::N_PER_ITEM) .chunks(T::N_PER_ITEM)
.map(|chunk| T::convert(chunk)) .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<Float> {
self.lookup_single(name, def) 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<i32> {
self.lookup_single(name, def) 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<bool> {
self.lookup_single(name, def) 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<String> {
self.lookup_single(name, def.to_string()) 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<Point2f> {
self.lookup_single(name, def) 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<Point3f> {
self.lookup_single(name, def) 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<Vector2f> {
self.lookup_single(name, def) 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<Vector3f> {
self.lookup_single(name, def) 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<Normal3f> {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_float_array(&self, name: &str) -> Vec<Float> { pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_int_array(&self, name: &str) -> Vec<i32> { pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_bool_array(&self, name: &str) -> Vec<bool> { pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_string_array(&self, name: &str) -> Vec<String> { pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_point2f_array(&self, name: &str) -> Vec<Point2f> { pub fn get_point2f_array(&self, name: &str) -> Result<Vec<Point2f>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_point3f_array(&self, name: &str) -> Vec<Point3f> { pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_vector3f_array(&self, name: &str) -> Vec<Vector3f> { pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_normal3f_array(&self, name: &str) -> Vec<Normal3f> { pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> {
self.lookup_array(name) self.lookup_array(name)
} }
@ -709,70 +704,73 @@ pub struct TextureParameterDictionary {
impl TextureParameterDictionary { impl TextureParameterDictionary {
pub fn new(dict: Arc<ParameterDictionary>, textures: Option<&NamedTextures>) -> Self { pub fn new(dict: Arc<ParameterDictionary>, 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<Float> {
self.dict.get_one_float(name, def) 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<i32> {
self.dict.get_one_int(name, def) 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<bool> {
self.dict.get_one_bool(name, def) 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<String> {
self.dict.get_one_string(name, def) self.dict.get_one_string(name, def)
} }
pub fn get_float_array(&self, name: &str) -> Vec<Float> { pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
self.dict.get_float_array(name) self.dict.get_float_array(name)
} }
pub fn get_int_array(&self, name: &str) -> Vec<i32> { pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> {
self.dict.get_int_array(name) self.dict.get_int_array(name)
} }
pub fn get_bool_array(&self, name: &str) -> Vec<bool> { pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> {
self.dict.get_bool_array(name) self.dict.get_bool_array(name)
} }
pub fn get_string_array(&self, name: &str) -> Vec<String> { pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> {
self.dict.get_string_array(name) 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<Point3f> {
self.dict.get_one_point3f(name, def) 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<Vector3f> {
self.dict.get_one_vector3f(name, def) 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<Normal3f> {
self.dict.get_one_normal3f(name, def) self.dict.get_one_normal3f(name, def)
} }
pub fn get_point3f_array(&self, name: &str) -> Vec<Point3f> { pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> {
self.dict.get_point3f_array(name) self.dict.get_point3f_array(name)
} }
pub fn get_vector3f_array(&self, name: &str) -> Vec<Vector3f> { pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> {
self.dict.get_vector3f_array(name) self.dict.get_vector3f_array(name)
} }
pub fn get_normal3f_array(&self, name: &str) -> Vec<Normal3f> { pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> {
self.dict.get_normal3f_array(name) 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<Point2f> {
self.dict.get_one_point2f(name, def) 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<Vector2f> {
self.dict.get_one_vector2f(name, def) self.dict.get_one_vector2f(name, def)
} }
@ -815,11 +813,25 @@ impl TextureParameterDictionary {
} }
} }
pub fn get_float_texture(&self, name: &str, val: Float) -> Arc<FloatTexture> { pub fn get_float_texture(&self, name: &str, val: Float) -> Result<Arc<FloatTexture>> {
if let Some(tex) = self.get_float_texture_or_null(name) { if let Some(tex) = self.get_float_texture_or_null(name)? {
return tex; return Ok(tex);
} else { } 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<Arc<FloatTexture>> {
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; return None;
} }
pub fn get_float_texture_or_null(&self, name: &str) -> Option<Arc<FloatTexture>> { pub fn get_float_texture_or_null(&self, name: &str) -> Result<Option<Arc<FloatTexture>>> {
for p in &self.dict.params { for p in &self.dict.params {
if p.name != name { if p.name != name {
continue; continue;
@ -932,19 +944,19 @@ impl TextureParameterDictionary {
if let Some(nt) = &self.textures { if let Some(nt) = &self.textures {
let map = &nt.float_textures; let map = &nt.float_textures;
if let Some(tex) = map.get(tex_name) { if let Some(tex) = map.get(tex_name) {
return Some(Arc::clone(tex)); return Ok(Some(Arc::clone(tex)));
} }
panic!( panic!(
"[{:?}] Couldn't find float texture named '{}'", "[{:?}] Couldn't find float texture named '{}'",
p.loc, tex_name p.loc, tex_name
); );
} }
return None; return Ok(None);
} }
"float" => { "float" => {
let v = self.get_one_float(name, 0.); let v = self.get_one_float(name, 0.)?;
return Some(Arc::new(FloatTexture::Constant(FloatConstantTexture::new( return Ok(Some(Arc::new(FloatTexture::Constant(
v, FloatConstantTexture::new(v),
)))); ))));
} }
_ => { _ => {
@ -952,6 +964,6 @@ impl TextureParameterDictionary {
} }
} }
} }
return None; Ok(None)
} }
} }

View file

@ -744,11 +744,12 @@ impl<'a> SceneParser<'a> {
} }
pub fn run(&mut self) -> Result<(), ParserError> { pub fn run(&mut self) -> Result<(), ParserError> {
let arena = Arc::new(Arena::new()); let arena = Arc::new(Arena::default());
loop { loop {
let token = match self.next_token()? { let token = match self.next_token()? {
Some(t) => t, Some(t) => t,
None => break, // EOF // EOF
None => break,
}; };
let first_char = token.text.chars().next().unwrap(); let first_char = token.text.chars().next().unwrap();

View file

@ -1,5 +1,6 @@
use crate::core::image::Image; 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 crate::utils::containers::Array2D;
use shared::Float; use shared::Float;
use shared::core::geometry::{Bounds2f, Point2i, Vector2f, Vector2i}; 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) Self::new_with_bounds(f.to_vec(), 0.0, 1.0)
} }
pub fn to_shared(&self, arena: &Arena) -> DevicePiecewiseConstant1D { pub fn to_shared<A: GpuAllocator>(&self, arena: &Arena<A>) -> DevicePiecewiseConstant1D {
let (func_ptr, _) = arena.alloc_slice(&self.func); let (func_ptr, _) = arena.alloc_slice(&self.func);
let (cdf_ptr, _) = arena.alloc_slice(&self.cdf); let (cdf_ptr, _) = arena.alloc_slice(&self.cdf);