Compare commits

..

No commits in common. "b36105edc169aa722cc69e2421a0bc8b1f32518d" and "0b04d54346ed4a2ae0682569cd821cc1a2983f2e" have entirely different histories.

23 changed files with 369 additions and 487 deletions

View file

@ -9,8 +9,6 @@ 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"
@ -35,12 +33,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,4 +15,6 @@ pub mod utils;
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
pub mod gpu; pub mod gpu;
pub use utils::{Arena, FileLoc, ParameterDictionary}; pub use utils::arena::Arena;
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, bail}; use anyhow::Result;
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 = let u_roughness = parameters
parameters.get_float_texture_with_fallback("uroughness", "roughness", 0.5)?; .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 v_roughness = let thickness = parameters.get_float_texture("thickness", 0.01);
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,76 +80,68 @@ impl CreateMaterial for CoatedConductorMaterial {
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Material> { ) -> Result<Material> {
let interface_u_roughness = parameters.get_float_texture_with_fallback( let interface_u_roughness = parameters
"interface.uroughness", .get_float_texture_or_null("interface.uroughness")
"interface.roughness", .unwrap_or_else(|| parameters.get_float_texture("interface.roughness", 0.));
0., let interface_v_roughness = parameters
)?; .get_float_texture_or_null("interface.vroughness")
let interface_v_roughness = parameters.get_float_texture_with_fallback( .unwrap_or_else(|| parameters.get_float_texture("interface.vroughness", 0.));
"interface.vroughness", let thickness = parameters.get_float_texture("interface.thickness", 0.01);
"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
let conductor_u_roughness = parameters.get_float_texture_with_fallback( .get_float_texture_or_null("conductor.uroughness")
"conductor.uroughness", .unwrap_or_else(|| parameters.get_float_texture("conductor.roughness", 0.));
"conductor.roughness", let conductor_v_roughness = parameters
0., .get_float_texture_or_null("conductor.vroughness")
)?; .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) = if reflectance.is_some() { let (conductor_eta, k) = match (&reflectance, conductor_eta, k) {
if conductor_eta.is_some() || k.is_some() { (Some(_), Some(_), _) | (Some(_), _, Some(_)) => {
bail!( return Err(error_exit(
"{}: For the coated conductor material, both \"reflectance\" \ Some(loc),
and \"eta\"/\"k\" cannot be provided simultaneously.", "For the coated conductor material, both \"reflectance\" \
loc and \"eta\" and \"k\" can't be provided.",
); ));
} }
(None, None)
} else { (None, eta, k) => {
let eta = conductor_eta.unwrap_or_else(|| { let final_eta = 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,15 +71,14 @@ impl CreateSampler for HaltonSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { let nsamp = options
n .quick_render
} else { .then_some(1)
params.get_one_int("pixelsamples", 16)? .or(options.pixel_samples)
}; .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,14 +11,12 @@ impl CreateSampler for IndependentSampler {
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 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,15 +12,13 @@ impl CreateSampler for SobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { let nsamp = options
n .quick_render
} else { .then_some(1)
params.get_one_int("pixelsamples", 16)? .or(options.pixel_samples)
}; .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,
@ -44,13 +42,13 @@ impl CreateSampler for PaddedSobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { let nsamp = options
n .quick_render
} else { .then_some(1)
params.get_one_int("pixelsamples", 16)? .or(options.pixel_samples)
}; .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,
@ -77,14 +75,13 @@ impl CreateSampler for ZSobolSampler {
_arena: &Arena, _arena: &Arena,
) -> Result<Sampler> { ) -> Result<Sampler> {
let options = get_options(); let options = get_options();
let nsamp = if let Some(n) = options.quick_render.then_some(1).or(options.pixel_samples) { let nsamp = options
n .quick_render
} else { .then_some(1)
params.get_one_int("pixelsamples", 16)? .or(options.pixel_samples)
}; .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,

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,7 +246,9 @@ 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) {
@ -288,7 +290,9 @@ 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> {
@ -506,7 +510,9 @@ 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,7 +2,6 @@ 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;
@ -25,8 +24,7 @@ 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<A: GpuAllocator> { pub struct Arena {
allocator: A,
inner: Mutex<ArenaInner>, inner: Mutex<ArenaInner>,
} }
@ -35,10 +33,9 @@ struct ArenaInner {
texture_cache: HashMap<usize, u64>, texture_cache: HashMap<usize, u64>,
} }
impl<A: GpuAllocator> Arena<A> { impl Arena {
pub fn new(allocator: A) -> Self { pub fn new() -> 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(),
@ -48,10 +45,12 @@ impl<A: GpuAllocator> Arena<A> {
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.allocator.alloc(layout) } as *mut T; let ptr = unsafe { self.alloc_unified(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)
} }
@ -68,13 +67,44 @@ impl<A: GpuAllocator> Arena<A> {
} }
let layout = Layout::array::<T>(values.len()).unwrap(); let layout = Layout::array::<T>(values.len()).unwrap();
let ptr = unsafe { self.allocator.alloc(layout) } as *mut T; let ptr = unsafe { self.alloc_unified(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();
@ -83,85 +113,107 @@ impl<A: GpuAllocator> Arena<A> {
return tex_obj; return tex_obj;
} }
// TODO: Backend-specific texture object creation. #[cfg(feature = "cuda")]
// CUDA: cudaCreateTextureObject let tex_obj = self.create_cuda_texture(mipmap);
// 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
} }
}
impl<A: GpuAllocator + Default> Default for Arena<A> { #[cfg(feature = "cuda")]
fn default() -> Self { fn create_cuda_texture(&self, mipmap: &MIPMap) -> u64 {
Self::new(A::default()) // TODO: Implement with cudarc
// 1. Get image data from mipmap.base_image()
// 2. cudaMallocArray
// 3. cudaMemcpy2DToArray
// 4. cudaCreateTextureObject
// 5. Return handle
0
} }
} }
impl<A: GpuAllocator> Drop for Arena<A> { impl Drop for Arena {
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(..) {
unsafe { self.allocator.dealloc(ptr, layout) }; 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 impl<A: GpuAllocator> Send for Arena<A> {} unsafe impl Send for Arena {}
unsafe impl<A: GpuAllocator> Sync for Arena<A> {} unsafe impl Sync for Arena {}
pub trait Upload { pub trait Upload {
type Target: Copy; type Target: Copy;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target>; fn upload(&self, arena: &Arena) -> Ptr<Self::Target>;
} }
impl Upload for Shape { impl Upload for Shape {
type Target = Shape; type Target = Shape;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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()),
@ -189,6 +241,15 @@ 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);
@ -223,7 +284,7 @@ impl Upload for SpectrumTexture {
impl Upload for FloatTexture { impl Upload for FloatTexture {
type Target = GPUFloatTexture; type Target = GPUFloatTexture;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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()),
@ -285,7 +346,7 @@ impl Upload for FloatTexture {
impl Upload for RGBToSpectrumTable { impl Upload for RGBToSpectrumTable {
type Target = RGBToSpectrumTable; type Target = RGBToSpectrumTable;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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) };
@ -305,7 +366,7 @@ impl Upload for RGBToSpectrumTable {
impl Upload for RGBColorSpace { impl Upload for RGBColorSpace {
type Target = RGBColorSpace; type Target = RGBColorSpace;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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 {
@ -326,7 +387,7 @@ impl Upload for RGBColorSpace {
impl Upload for DeviceStandardColorSpaces { impl Upload for DeviceStandardColorSpaces {
type Target = DeviceStandardColorSpaces; type Target = DeviceStandardColorSpaces;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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);
@ -346,7 +407,7 @@ impl Upload for DeviceStandardColorSpaces {
impl Upload for PiecewiseConstant2D { impl Upload for PiecewiseConstant2D {
type Target = DevicePiecewiseConstant2D; type Target = DevicePiecewiseConstant2D;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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
@ -369,7 +430,7 @@ impl Upload for PiecewiseConstant2D {
impl Upload for WindowedPiecewiseConstant2D { impl Upload for WindowedPiecewiseConstant2D {
type Target = DeviceWindowedPiecewiseConstant2D; type Target = DeviceWindowedPiecewiseConstant2D;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> Ptr<Self::Target> {
let specific = DeviceWindowedPiecewiseConstant2D { let specific = DeviceWindowedPiecewiseConstant2D {
sat: self.sat, sat: self.sat,
func: self.func, func: self.func,
@ -381,7 +442,7 @@ impl Upload for WindowedPiecewiseConstant2D {
impl Upload for TriangleMesh { impl Upload for TriangleMesh {
type Target = DeviceTriangleMesh; type Target = DeviceTriangleMesh;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> Ptr<Self::Target> {
let storage = &self.storage; let storage = &self.storage;
// Upload all arrays to arena // Upload all arrays to arena
@ -429,7 +490,7 @@ impl Upload for TriangleMesh {
impl Upload for BilinearPatchMesh { impl Upload for BilinearPatchMesh {
type Target = DeviceBilinearPatchMesh; type Target = DeviceBilinearPatchMesh;
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> 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);
@ -464,7 +525,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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> Ptr<Self::Target> {
match self { match self {
Some(val) => val.upload(arena), Some(val) => val.upload(arena),
None => Ptr::null(), None => Ptr::null(),
@ -475,7 +536,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<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload(&self, arena: &Arena) -> Ptr<Self::Target> {
(**self).upload(arena) (**self).upload(arena)
} }
} }

View file

@ -1,147 +0,0 @@
use std::alloc::Layout;
pub trait GpuAllocator: Send + Sync {
/// Allocate `size` bytes with given alignment.
/// Returns a host-mapped pointer.
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
}
/// CPU fallback — standard system allocator.
pub struct SystemAllocator;
impl Default for SystemAllocator {
fn default() -> Self {
Self
}
}
impl GpuAllocator for SystemAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.size() == 0 {
return layout.align() as *mut u8;
}
unsafe { std::alloc::alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.size() > 0 {
unsafe {
std::alloc::dealloc(ptr, layout);
}
}
}
}
/// CUDA unified memory backend using CudaAllocator
#[cfg(feature = "cuda")]
pub struct CudaAllocator;
#[cfg(feature = "cuda")]
impl Default for CudaAllocator {
fn default() -> Self {
Self
}
}
#[cfg(feature = "cuda")]
impl GpuAllocator for CudaAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
use cust::memory::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();
std::mem::forget(unified_ptr);
raw
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
use cust::memory::{UnifiedPointer, cuda_free_unified};
if layout.size() > 0 {
let _ = unsafe { cuda_free_unified(UnifiedPointer::wrap(ptr)) };
}
}
}
/// Vulkan backend using gpu-allocator.
#[cfg(feature = "vulkan")]
pub mod vulkan {
use super::GpuAllocator;
use ash::vk;
use gpu_allocator::MemoryLocation;
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
use parking_lot::Mutex;
use std::alloc::Layout;
use std::collections::HashMap;
pub struct VulkanAllocator {
allocator: Mutex<Allocator>,
/// Track pointer -> Allocation so we can free later.
allocations: Mutex<HashMap<usize, Allocation>>,
}
impl VulkanAllocator {
pub fn new(allocator: Allocator) -> Self {
Self {
allocator: Mutex::new(allocator),
allocations: Mutex::new(HashMap::new()),
}
}
}
impl Default for VulkanAllocator {
fn default() -> Self {
Self
}
}
impl GpuAllocator for VulkanAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let size = layout.size().max(layout.align());
if size == 0 {
return layout.align() as *mut u8;
}
let mut alloc = self.allocator.lock();
let allocation = alloc
.allocate(&AllocationCreateDesc {
name: "arena",
requirements: vk::MemoryRequirements {
size: size as u64,
alignment: layout.align() as u64,
memory_type_bits: u32::MAX,
},
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
})
.expect("Vulkan allocation failed");
let ptr = allocation
.mapped_ptr()
.expect("Vulkan allocation not host-mapped")
.as_ptr() as *mut u8;
self.allocations.lock().insert(ptr as usize, allocation);
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.size() == 0 {
return;
}
if let Some(allocation) = self.allocations.lock().remove(&(ptr as usize)) {
self.allocator
.lock()
.free(allocation)
.expect("Vulkan free failed");
}
}
}
}

View file

@ -1,5 +1,4 @@
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;
@ -12,19 +11,10 @@ pub mod parser;
pub mod sampling; pub mod sampling;
pub mod strings; pub mod strings;
pub use arena::Upload; pub use arena::{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,7 +3,6 @@ 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};
@ -20,12 +19,14 @@ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
}; };
pub fn error_exit(loc: Option<&FileLoc>, message: &str) -> String { pub fn error_exit(loc: Option<&FileLoc>, message: &str) -> ! {
if let Some(l) = loc { if let Some(l) = loc {
format!("{}: {}", l, message) log::error!("Error: {}: {}", l, message);
} else { } else {
message.to_string() log::error!("Error: {}", message);
} }
std::process::exit(1);
} }
#[derive(Debug)] #[derive(Debug)]
@ -249,10 +250,7 @@ pub struct ParameterDictionary {
} }
impl ParameterDictionary { impl ParameterDictionary {
pub fn new( pub fn new(mut params: ParsedParameterVector, color_space: Option<Arc<RGBColorSpace>>) -> Self {
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 {
@ -260,15 +258,15 @@ impl ParameterDictionary {
color_space, color_space,
n_owned_params, n_owned_params,
}; };
dict.check_parameter_types()?; dict.check_parameter_types();
Ok(dict) 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>>,
) -> Result<Self> { ) -> 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());
@ -277,19 +275,21 @@ impl ParameterDictionary {
color_space, color_space,
n_owned_params, n_owned_params,
}; };
dict.check_parameter_types()?; dict.check_parameter_types();
Ok(dict) dict
} }
fn check_parameter_types(&self) -> Result<()> { fn check_parameter_types(&self) {
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() {
bail!( error_exit(
"{}: non-Boolean values provided for Boolean-valued parameter \"{}\"", Some(&p.loc),
&p.loc, &format!(
"\"{}\": non-Boolean values provided for Boolean-valued parameter",
p.name p.name
),
); );
} }
} }
@ -304,49 +304,51 @@ impl ParameterDictionary {
| "rgb" | "rgb"
| "blackbody" => { | "blackbody" => {
if p.ints.is_empty() && p.floats.is_empty() { if p.ints.is_empty() && p.floats.is_empty() {
bail!( error_exit(
"{}: non-numeric values provided for numeric-valued parameter \"{}\"", Some(&p.loc),
&p.loc, &format!(
"\"{}\": 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() {
bail!( error_exit(
"{}: non-string values provided for string-valued parameter \"{}\"", Some(&p.loc),
&p.loc, &format!(
"\"{}\": 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() {
bail!( error_exit(
"{}: expecting string or numeric-valued parameter for spectrum parameter \"{}\"", Some(&p.loc),
&p.loc, &format!(
"\"{}\": expecting string or numeric-valued parameter for spectrum parameter",
p.name p.name
),
); );
} }
} }
unknown => { unknown => {
bail!( error_exit(
"{}: unknown parameter type \"{}\" '{}'", Some(&p.loc),
&p.loc, &format!("\"{}\": unknown parameter type '{}'", p.name, unknown),
p.name,
unknown,
); );
} }
} }
} }
Ok(())
} }
fn lookup_single<T>(&self, name: &str, default_val: T) -> Result<T> fn lookup_single<T>(&self, name: &str, default_val: T) -> T
where where
T: PBRTParameter, T: PBRTParameter,
{ {
@ -355,31 +357,32 @@ impl ParameterDictionary {
let values = T::get_values(&param); let values = T::get_values(&param);
if values.is_empty() { if values.is_empty() {
bail!( error_exit(
"{}: No values provided for parameter \"{}\".", Some(&param.loc),
&param.loc, &format!("No values provided for parameter \"{}\".", name),
name
); );
} }
if values.len() != T::N_PER_ITEM { if values.len() != T::N_PER_ITEM {
bail!( error_exit(
"{}: Expected {} values for parameter \"{}\". Found {}.", Some(&param.loc),
&param.loc, &format!(
"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 Ok(T::convert(values)); return T::convert(values);
} }
Ok(default_val) default_val
} }
pub fn lookup_array<T>(&self, name: &str) -> Result<Vec<T>> pub fn lookup_array<T>(&self, name: &str) -> Vec<T>
where where
T: PBRTParameter, T: PBRTParameter,
{ {
@ -388,90 +391,92 @@ 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 {
bail!( error_exit(
"{}: Number of values for \"{}\" is not a multiple of {}", Some(&param.loc),
&param.loc, &format!(
"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 Ok(values return values
.chunks(T::N_PER_ITEM) .chunks(T::N_PER_ITEM)
.map(|chunk| T::convert(chunk)) .map(|chunk| T::convert(chunk))
.collect()); .collect();
} }
} }
Ok(Vec::new()) Vec::new()
} }
pub fn get_one_float(&self, name: &str, def: Float) -> Result<Float> { pub fn get_one_float(&self, name: &str, def: Float) -> Float {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_int(&self, name: &str, def: i32) -> Result<i32> { pub fn get_one_int(&self, name: &str, def: i32) -> i32 {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_bool(&self, name: &str, def: bool) -> Result<bool> { pub fn get_one_bool(&self, name: &str, def: bool) -> bool {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_string(&self, name: &str, def: &str) -> Result<String> { pub fn get_one_string(&self, name: &str, def: &str) -> 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) -> Result<Point2f> { pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Point2f {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result<Point3f> { pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Point3f {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Result<Vector2f> { pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Vector2f {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Result<Vector3f> { pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Vector3f {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Result<Normal3f> { pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Normal3f {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> { pub fn get_float_array(&self, name: &str) -> Vec<Float> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> { pub fn get_int_array(&self, name: &str) -> Vec<i32> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> { pub fn get_bool_array(&self, name: &str) -> Vec<bool> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> { pub fn get_string_array(&self, name: &str) -> Vec<String> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_point2f_array(&self, name: &str) -> Result<Vec<Point2f>> { pub fn get_point2f_array(&self, name: &str) -> Vec<Point2f> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> { pub fn get_point3f_array(&self, name: &str) -> Vec<Point3f> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> { pub fn get_vector3f_array(&self, name: &str) -> Vec<Vector3f> {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> { pub fn get_normal3f_array(&self, name: &str) -> Vec<Normal3f> {
self.lookup_array(name) self.lookup_array(name)
} }
@ -704,73 +709,70 @@ 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 { Self { dict, textures: textures.cloned() }
dict,
textures: textures.cloned(),
}
} }
pub fn get_one_float(&self, name: &str, def: Float) -> Result<Float> { pub fn get_one_float(&self, name: &str, def: Float) -> 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) -> Result<i32> { pub fn get_one_int(&self, name: &str, def: i32) -> 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) -> Result<bool> { pub fn get_one_bool(&self, name: &str, def: bool) -> 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) -> Result<String> { pub fn get_one_string(&self, name: &str, def: &str) -> String {
self.dict.get_one_string(name, def) self.dict.get_one_string(name, def)
} }
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> { pub fn get_float_array(&self, name: &str) -> Vec<Float> {
self.dict.get_float_array(name) self.dict.get_float_array(name)
} }
pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> { pub fn get_int_array(&self, name: &str) -> Vec<i32> {
self.dict.get_int_array(name) self.dict.get_int_array(name)
} }
pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> { pub fn get_bool_array(&self, name: &str) -> Vec<bool> {
self.dict.get_bool_array(name) self.dict.get_bool_array(name)
} }
pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> { pub fn get_string_array(&self, name: &str) -> Vec<String> {
self.dict.get_string_array(name) self.dict.get_string_array(name)
} }
pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result<Point3f> { pub fn get_one_point3f(&self, name: &str, def: Point3f) -> 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) -> Result<Vector3f> { pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> 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) -> Result<Normal3f> { pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Normal3f {
self.dict.get_one_normal3f(name, def) self.dict.get_one_normal3f(name, def)
} }
pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> { pub fn get_point3f_array(&self, name: &str) -> Vec<Point3f> {
self.dict.get_point3f_array(name) self.dict.get_point3f_array(name)
} }
pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> { pub fn get_vector3f_array(&self, name: &str) -> Vec<Vector3f> {
self.dict.get_vector3f_array(name) self.dict.get_vector3f_array(name)
} }
pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> { pub fn get_normal3f_array(&self, name: &str) -> Vec<Normal3f> {
self.dict.get_normal3f_array(name) self.dict.get_normal3f_array(name)
} }
pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Result<Point2f> { pub fn get_one_point2f(&self, name: &str, def: Point2f) -> 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) -> Result<Vector2f> { pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Vector2f {
self.dict.get_one_vector2f(name, def) self.dict.get_one_vector2f(name, def)
} }
@ -813,25 +815,11 @@ impl TextureParameterDictionary {
} }
} }
pub fn get_float_texture(&self, name: &str, val: Float) -> Result<Arc<FloatTexture>> { pub fn get_float_texture(&self, name: &str, val: Float) -> 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 Ok(tex); return tex;
} else { } else {
return Ok(Arc::new(FloatTexture::Constant(FloatConstantTexture::new( return Arc::new(FloatTexture::Constant(FloatConstantTexture::new(val)));
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),
} }
} }
@ -921,7 +909,7 @@ impl TextureParameterDictionary {
return None; return None;
} }
pub fn get_float_texture_or_null(&self, name: &str) -> Result<Option<Arc<FloatTexture>>> { pub fn get_float_texture_or_null(&self, name: &str) -> 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;
@ -944,19 +932,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 Ok(Some(Arc::clone(tex))); return 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 Ok(None); return None;
} }
"float" => { "float" => {
let v = self.get_one_float(name, 0.)?; let v = self.get_one_float(name, 0.);
return Ok(Some(Arc::new(FloatTexture::Constant( return Some(Arc::new(FloatTexture::Constant(FloatConstantTexture::new(
FloatConstantTexture::new(v), v,
)))); ))));
} }
_ => { _ => {
@ -964,6 +952,6 @@ impl TextureParameterDictionary {
} }
} }
} }
Ok(None) return None;
} }
} }

View file

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

View file

@ -1,6 +1,5 @@
use crate::core::image::Image; use crate::core::image::Image;
use crate::utils::arena::Arena; use crate::utils::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};
@ -24,7 +23,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<A: GpuAllocator>(&self, arena: &Arena<A>) -> DevicePiecewiseConstant1D { pub fn to_shared(&self, arena: &Arena) -> 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);