Implementing arena based memory allocation
This commit is contained in:
parent
f7c47be077
commit
0ef563d1a5
9 changed files with 1036 additions and 179 deletions
|
|
@ -1,55 +1,34 @@
|
|||
use shared::Float;
|
||||
use shared::core::ligh::{Light, LightBase, LightType};
|
||||
use shared::core::medium::MediumInterface;
|
||||
use shared::core::shape::Shape;
|
||||
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::lights::DiffuseAreaLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::core::image::{Image, ImageIO};
|
||||
use crate::core::light::{CreateLight, lookup_spectrum};
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::{
|
||||
FloatTexture, FloatTextureTrait, TextureEvalContext, TextureEvaluator,
|
||||
UniversalTextureEvaluator,
|
||||
};
|
||||
use crate::shapes::{Shape, ShapeSample, ShapeSampleContext, ShapeTrait};
|
||||
use crate::utils::hash::hash_float;
|
||||
use shared::core::color::RGB;
|
||||
use shared::core::geometry::{
|
||||
Bounds3f, Normal3f, Point2f, Point2fi, Point2i, Point3f, Point3fi, Ray, Vector3f, VectorLike,
|
||||
};
|
||||
use shared::core::interaction::{
|
||||
Interaction, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||
};
|
||||
use shared::core::light::{
|
||||
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
||||
};
|
||||
use shared::core::medium::MediumInterface;
|
||||
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||
use shared::images::Image;
|
||||
use shared::spectra::{
|
||||
DenselySampledSpectrum, RGBColorSpace, RGBIlluminantSpectrum, SampledSpectrum,
|
||||
SampledWavelengths,
|
||||
};
|
||||
use shared::{Float, PI};
|
||||
use std::sync::Arc;
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseAreaLightStorage {
|
||||
shape: Arc<Shape>,
|
||||
alpha: Option<Arc<GPUFloatTexture>>,
|
||||
image: Option<Arc<Image>>,
|
||||
image_color_space: Option<Arc<RGBColorSpace>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseAreaLightHost {
|
||||
pub view: DiffuseAreaLight,
|
||||
_storage: DiffuseAreaLightStorage,
|
||||
}
|
||||
|
||||
impl DiffuseAreaLightHost {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
render_from_light: Transform,
|
||||
impl CreateLight for DiffuseAreaLight {
|
||||
fn new(
|
||||
render_from_light: shared::utils::Transform,
|
||||
medium_interface: MediumInterface,
|
||||
le: Spectrum,
|
||||
scale: Float,
|
||||
shape: Shape,
|
||||
alpha: FloatTexture,
|
||||
image: Option<Image>,
|
||||
image_color_space: Option<Arc<RGBColorSpace>>,
|
||||
two_sided: bool,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<Float>,
|
||||
) -> Self {
|
||||
let is_constant_zero = match &alpha {
|
||||
FloatTexture::FloatConstant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
|
||||
|
|
@ -91,22 +70,111 @@ impl DiffuseAreaLightHost {
|
|||
);
|
||||
}
|
||||
|
||||
let storage = DiffuseAreaLightStorage {
|
||||
shape,
|
||||
alpha: stored_alpha,
|
||||
image,
|
||||
image_color_space,
|
||||
};
|
||||
|
||||
let view = DiffuseAreaLight {
|
||||
Self {
|
||||
base,
|
||||
area: shape.area(),
|
||||
image,
|
||||
image_color_space,
|
||||
shape: Ptr::from(&*storage.shape),
|
||||
};
|
||||
|
||||
Self {
|
||||
view,
|
||||
_storage: storage,
|
||||
alpha: stored_alpha,
|
||||
lemit,
|
||||
two_sided,
|
||||
scale,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
params: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_text: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
||||
let mut scale = params.get_one_float("scale", 1.);
|
||||
let two_sided = params.get_one_bool("twosided", false);
|
||||
let filename = resolve_filename(params.get_one_string("filename", ""));
|
||||
let mut image_color_space = colorspace.clone();
|
||||
if !filename.is_empty() {
|
||||
if l.is_some() {
|
||||
panic!(
|
||||
"{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.",
|
||||
loc
|
||||
);
|
||||
}
|
||||
let im = Image::read(filename, None);
|
||||
let image: Image = im.image;
|
||||
if image.has_any_infinite_pixels() {
|
||||
panic!(
|
||||
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||
loc, filename
|
||||
);
|
||||
}
|
||||
let channel_desc = image.get_channel_desc(&["R", "G", "B"]).expect(&format!(
|
||||
"{:?}: Image '{}' must have R, G, B channels",
|
||||
loc, filename
|
||||
));
|
||||
let selected_image = image.select_channels(channel_desc);
|
||||
image_color_space = im.metadata.colorspace;
|
||||
} else if l.is_none() {
|
||||
l = Some(colorpace.unwrap().Illuminant.clone());
|
||||
}
|
||||
|
||||
let l_for_scale = l.as_ref().unwrap_or(&colorspace.illuminant);
|
||||
let lemit_data = lookup_spectrum(l_for_scale);
|
||||
scale /= spectrum_to_photometric(lemit_data);
|
||||
|
||||
let phi_v = parameters.get_one_float("power", -1.0);
|
||||
if phi_v > 0.0 {
|
||||
// k_e is the emissive power of the light as defined by the spectral
|
||||
// distribution and texture and is used to normalize the emitted
|
||||
// radiance such that the user-defined power will be the actual power
|
||||
// emitted by the light.
|
||||
|
||||
let mut k_e: Float = 1.0;
|
||||
|
||||
if let Some(ref img) = image_host {
|
||||
// Get the appropriate luminance vector from the image colour space
|
||||
let lum_vec = image_color_space.luminance_vector();
|
||||
|
||||
let mut sum_k_e = 0.0;
|
||||
let res = img.resolution;
|
||||
|
||||
for y in 0..res.y {
|
||||
for x in 0..res.x {
|
||||
let r = img.get_channel(Point2i::new(x, y), 0);
|
||||
let g = img.get_channel(Point2i::new(x, y), 1);
|
||||
let b = img.get_channel(Point2i::new(x, y), 2);
|
||||
|
||||
sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2];
|
||||
}
|
||||
}
|
||||
k_e = sum_k_e / (res.x * res.y) as Float;
|
||||
}
|
||||
|
||||
let side_factor = if two_sided { 2.0 } else { 1.0 };
|
||||
k_e *= side_factor * shape_data.area() * PI;
|
||||
|
||||
// now multiply up scale to hit the target power
|
||||
scale *= phi_v / k_e;
|
||||
}
|
||||
|
||||
let specific = DiffuseAreaLight::new(
|
||||
render_from_light,
|
||||
medium.into(),
|
||||
l,
|
||||
scale,
|
||||
shape.upload(arena),
|
||||
alpha.upload(arena),
|
||||
image.upload(arena),
|
||||
image_color_space.upload(arena),
|
||||
Some(true),
|
||||
shape.area(),
|
||||
);
|
||||
|
||||
Light::DiffuseArea(specific)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
src/lights/distant.rs
Normal file
108
src/lights/distant.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use crate::core::light::{CreateLight, lookup_spectrum};
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::FloatTexture;
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||
use shared::core::geometry::{Vector3f, VectorLike};
|
||||
use shared::core::light::{Light, LightBase};
|
||||
use shared::core::medium::{Medium, MediumInterface};
|
||||
use shared::core::shape::Shape;
|
||||
use shared::lights::DistantLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::Transform;
|
||||
|
||||
impl CreateLight for DistantLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: shared::core::medium::MediumInterface,
|
||||
le: shared::core::spectrum::Spectrum,
|
||||
scale: shared::Float,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<shared::Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::DeltaDirection,
|
||||
render_from_light,
|
||||
MediumInterface::empty(),
|
||||
);
|
||||
let lemit = lookup_spectrum(le);
|
||||
Self {
|
||||
base,
|
||||
lemit,
|
||||
scale,
|
||||
scene_center: Vector3f::default(),
|
||||
scene_radius: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_text: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
let l = parameters
|
||||
.get_one_spectrum(
|
||||
"L",
|
||||
colorspace.unwrap().illuminant,
|
||||
SpectrumType::Illuminant,
|
||||
)
|
||||
.unwrap();
|
||||
let lemit = lookup_spectrum(l);
|
||||
let mut scale = parameters.get_one_float("scale", 1);
|
||||
|
||||
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.));
|
||||
let to = parameters.get_one_point3f("to", Point3f(0., 0., 1.));
|
||||
let w = (from - to).normalize();
|
||||
let (v1, v2) = w.coordinate_system();
|
||||
let m: [Float; 16] = [
|
||||
v1.x(),
|
||||
v2.x(),
|
||||
w.x(),
|
||||
0.,
|
||||
v1.y(),
|
||||
v2.y(),
|
||||
w.y(),
|
||||
0.,
|
||||
v1.z(),
|
||||
v2.z(),
|
||||
w.z(),
|
||||
0.,
|
||||
0.,
|
||||
0.,
|
||||
0.,
|
||||
1.,
|
||||
];
|
||||
let t = Transform::from_flat(m);
|
||||
let final_render = render_from_light * t;
|
||||
scale /= spectrum_to_photometric(l.unwrap());
|
||||
// Adjust scale to meet target illuminance value
|
||||
// Like for IBLs we measure illuminance as incident on an upward-facing
|
||||
// patch.
|
||||
let e_v = parameters.get_one_float("illuminance", -1.);
|
||||
if e_v > 0. {
|
||||
sc *= e_v;
|
||||
}
|
||||
let specific = DistantLight::new(
|
||||
final_render,
|
||||
medium.into(),
|
||||
le,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
Light::Distant(specific)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,31 @@
|
|||
use crate::core::light::{LightBaseTrait, LightFactory};
|
||||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||
use crate::core::light::{CreateLight, lookup_spectrum};
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::FloatTexture;
|
||||
use crate::utils::sampling::PiecewiseConstant2D;
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
||||
use shared::core::image::ImageBase;
|
||||
use shared::core::light::{Light, LightBase, LightType};
|
||||
use shared::core::medium::{Medium, MediumInterface};
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::lights::GoniometricLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::Transform;
|
||||
use shared::utils::containers::Array2D;
|
||||
|
||||
impl LightFactory for GoniometricLight {
|
||||
impl CreateLight for GoniometricLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: MediumInterface,
|
||||
scale: Float,
|
||||
iemit: &Spectrum,
|
||||
image: &Image,
|
||||
le: Spectrum,
|
||||
scale: shared::Float,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<shared::Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::DeltaPosition,
|
||||
|
|
@ -14,15 +33,142 @@ impl LightFactory for GoniometricLight {
|
|||
medium_interface,
|
||||
);
|
||||
|
||||
let i_interned = LightBase::lookup_spectrum(&iemit);
|
||||
let d = image.get_sampling_distribution_uniform();
|
||||
let distrib = PiecewiseConstant2D::new_with_data(&d);
|
||||
let iemit = lookup_spectrum(le);
|
||||
let d = image.unwrap().get_sampling_distribution_uniform();
|
||||
let distrib = PiecewiseConstant2D::new_with_data(d);
|
||||
Self {
|
||||
base,
|
||||
iemit: i_interned,
|
||||
iemit,
|
||||
scale,
|
||||
image: Ptr::from(image),
|
||||
image,
|
||||
distrib,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
params: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_text: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
let i = params.get_one_spectrum(
|
||||
"I",
|
||||
colorspace.unwrap().illuminant,
|
||||
SpectrumType::Illuminant,
|
||||
);
|
||||
let lemit_data = lookup_spectrum(&i_spectrum_def);
|
||||
let sc = params.get_one_float("scale", 1.);
|
||||
let filename = resolve_filename(params.get_one_string("filename", ""));
|
||||
let mut image: Option<Image> = None;
|
||||
|
||||
if filename.is_empty() {
|
||||
println!(
|
||||
"{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.",
|
||||
loc
|
||||
)
|
||||
} else {
|
||||
let im = Image::read(filename, None).expect("Could not load image");
|
||||
let loaded_img: Image = im.image;
|
||||
let res = loaded_img.resolution();
|
||||
let metadata: ImageMetadata = im.metadata;
|
||||
if loaded_img.has_any_infinite_pixels() {
|
||||
panic!(
|
||||
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||
loc, filename
|
||||
);
|
||||
}
|
||||
if res.x() != res.y() {
|
||||
panic!(
|
||||
"{:?}: Image resolution ({}, {}) is non square. Unlikely that this is an equal area map.",
|
||||
loc,
|
||||
res.x(),
|
||||
res.y(),
|
||||
y()
|
||||
);
|
||||
}
|
||||
let rgb_desc = loaded_img.get_channel_desc(&["R", "G", "B"]);
|
||||
let y_desc = loaded_img.get_channel_desc(&["Y"]);
|
||||
if let Ok(rgb) = rgb_desc {
|
||||
if y_desc.is_ok() {
|
||||
panic!(
|
||||
"{:?}: Image '{}' has both RGB and Y channels. Ambiguous.",
|
||||
loc, filename
|
||||
);
|
||||
}
|
||||
|
||||
let mut y_pixels = Vec::with_capacity((res.x * res.y) as usize);
|
||||
|
||||
for y in 0..res.y {
|
||||
for x in 0..res.x {
|
||||
let r = loaded_img.get_channel(Point2i::new(x, y), 0);
|
||||
let g = loaded_img.get_channel(Point2i::new(x, y), 1);
|
||||
let b = loaded_img.get_channel(Point2i::new(x, y), 2);
|
||||
|
||||
y_pixels.push((r + g + b) / 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
loaded_img = Some(Image::new(
|
||||
PixelFormat::F32,
|
||||
res,
|
||||
&["Y"],
|
||||
ColorEncoding::Linear,
|
||||
));
|
||||
} else if y_desc.is_ok() {
|
||||
image = Some(loaded_img);
|
||||
} else {
|
||||
panic!(
|
||||
"{:?}: Image '{}' has neither RGB nor Y channels.",
|
||||
loc, filename
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
scale /= spectrum_to_photometric(&lemit_data);
|
||||
let phi_v = params.get_one_float("power", -1.0);
|
||||
|
||||
if phi_v > 0.0 {
|
||||
if let Some(ref img) = image {
|
||||
let mut sum_y = 0.0;
|
||||
let res = img.resolution();
|
||||
|
||||
for y in 0..res.y {
|
||||
for x in 0..res.x {
|
||||
sum_y += img.get_channel(Point2i::new(x, y), 0);
|
||||
}
|
||||
}
|
||||
|
||||
let k_e = 4.0 * PI * sum_y / (res.x * res.y) as Float;
|
||||
scale *= phi_v / k_e;
|
||||
}
|
||||
}
|
||||
|
||||
let swap_yz: [Float; 16] = [
|
||||
1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1.,
|
||||
];
|
||||
let t = Transform::from_flat(swap_yz);
|
||||
let final_render_from_light = render_from_light * t;
|
||||
|
||||
let d: Array2D<Float> = image.get_sampling_distribution();
|
||||
distrib = PiecewiseConstant2D::new_with_data(d);
|
||||
|
||||
let specific = GoniometricLight::new(
|
||||
render_from_light,
|
||||
medium_interface,
|
||||
le,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
Some(image),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
Light::Goniometric(specific)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,27 @@
|
|||
use shared::Float;
|
||||
use shared::core::geometry::{Bounds3f, Point2f};
|
||||
use shared::core::light::{LightBase, LightType};
|
||||
use shared::core::light::{CreateLight, Light, LightBase, LightType};
|
||||
use shared::core::medium::MediumInterface;
|
||||
use shared::lights::{InfiniteImageLight, InfinitePortalLight, InfiniteUniformLight};
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::Transform;
|
||||
use shared::utils::sampling::PiecewiseConstant2D;
|
||||
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::core::light::{LightBaseTrait, LightFactory};
|
||||
use crate::core::light::{LightBaseTrait, lookup_spectrum};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InfiniteImageLightStorage {
|
||||
image: Image,
|
||||
distrib: PiecewiseConstant2D,
|
||||
compensated_distrib: PiecewiseConstant2D,
|
||||
image_color_space: RGBColorSpace,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InfiniteImageLightHost {
|
||||
pub view: InfiniteImageLight,
|
||||
pub filename: String, // Kept on Host only
|
||||
_storage: Arc<InfiniteImageLightStorage>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for InfiniteImageLightHost {
|
||||
type Target = InfiniteImageLight;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view
|
||||
}
|
||||
}
|
||||
|
||||
impl InfiniteImageLightHost {
|
||||
pub fn new(
|
||||
impl CreateLight for InfiniteImageLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
image: Image,
|
||||
image_color_space: RGBColorSpace,
|
||||
medium_interface: MediumInterface,
|
||||
le: shared::core::spectrum::Spectrum,
|
||||
scale: Float,
|
||||
filename: String,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::Infinite,
|
||||
|
|
@ -53,6 +36,7 @@ impl InfiniteImageLightHost {
|
|||
assert_eq!(3, desc.size());
|
||||
assert!(desc.is_identity());
|
||||
if image.resolution().x() != image.resolution().y() {
|
||||
hash(hashee, into);
|
||||
panic!(
|
||||
"{}: image resolution ({}, {}) is non-square. It's unlikely this is an equal area environment map.",
|
||||
filename,
|
||||
|
|
@ -62,7 +46,7 @@ impl InfiniteImageLightHost {
|
|||
}
|
||||
let mut d = image.get_sampling_distribution_uniform();
|
||||
let domain = Bounds2f::from_points(Point2f::new(0., 0.), Point2f::new(1., 1.));
|
||||
let distrib = PiecewiseConstant2D::new_with_bounds(&d, domain);
|
||||
let distrib = DevicePiecewiseConstant2D::new_with_bounds(&d, domain);
|
||||
let slice = &mut d.values; // or d.as_slice_mut()
|
||||
let count = slice.len() as Float;
|
||||
let sum: Float = slice.iter().sum();
|
||||
|
|
@ -79,31 +63,31 @@ impl InfiniteImageLightHost {
|
|||
}
|
||||
}
|
||||
|
||||
let compensated_distrib = PiecewiseConstant2D::new_with_bounds(&d, domain);
|
||||
let compensated_distrib = DevicePiecewiseConstant2D::new_with_bounds(&d, domain);
|
||||
|
||||
let storage = Arc::new(InfiniteImageLightStorage {
|
||||
image,
|
||||
distrib,
|
||||
compensated_distrib,
|
||||
image_color_space,
|
||||
});
|
||||
|
||||
let view = InfiniteImageLight {
|
||||
InfiniteImageLight {
|
||||
base,
|
||||
image: &storage.image,
|
||||
image: &image,
|
||||
image_color_space: &storage.image_color_space,
|
||||
scene_center: Point3f::default(),
|
||||
scene_radius: 0.,
|
||||
scale,
|
||||
distrib: &storage.distrib,
|
||||
compensated_distrib: &storage.compensated_distrib,
|
||||
distrib: &distrib,
|
||||
compensated_distrib: &compensated_distrib,
|
||||
};
|
||||
|
||||
Self {
|
||||
view,
|
||||
filename,
|
||||
_storage: storage,
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut crate::utils::Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &crate::utils::ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> shared::core::light::Light {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,21 +105,18 @@ pub struct InfinitePortalLightHost {
|
|||
_storage: Arc<InfinitePortalLightStorage>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for InfinitePortalLightHost {
|
||||
type Target = InfinitePortalLight;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view
|
||||
}
|
||||
}
|
||||
|
||||
impl InfinitePortalLightHost {
|
||||
pub fn new(
|
||||
impl CreateLight for InfinitePortalLightHost {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
equal_area_image: &Image,
|
||||
image_color_space: RGBColorSpace,
|
||||
medium_interface: MediumInterface,
|
||||
le: shared::core::spectrum::Spectrum,
|
||||
scale: Float,
|
||||
filename: String,
|
||||
points: Vec<Point3f>,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::Infinite,
|
||||
|
|
@ -250,34 +231,54 @@ impl InfinitePortalLightHost {
|
|||
image_color_space,
|
||||
});
|
||||
|
||||
let view = InfinitePortalLight {
|
||||
InfinitePortalLight {
|
||||
base,
|
||||
image: &storage.image,
|
||||
image,
|
||||
image_color_space: &storage.image_color_space,
|
||||
scale,
|
||||
scene_center: Point3f::default(),
|
||||
scene_radius: 0.,
|
||||
portal,
|
||||
portal_frame,
|
||||
distribution: &storage.distribution,
|
||||
};
|
||||
|
||||
Self {
|
||||
view,
|
||||
filename,
|
||||
_storage: storage,
|
||||
distribution,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut crate::utils::Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &crate::utils::ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LightFactory for InfiniteUniformLight {
|
||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self {
|
||||
impl CreateLight for InfiniteUniformLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: MediumInterface,
|
||||
le: shared::core::spectrum::Spectrum,
|
||||
scale: Float,
|
||||
shape: Option<shared::utils::Ptr<Shape>>,
|
||||
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
image: Option<shared::utils::Ptr<Image>>,
|
||||
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<Float>,
|
||||
cos_fallof_start: Option<Float>,
|
||||
total_width: Option<Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::Infinite,
|
||||
render_from_light,
|
||||
MediumInterface::default(),
|
||||
&render_from_light,
|
||||
&MediumInterface::default(),
|
||||
);
|
||||
let lemit = LightBase::lookup_spectrum(&le);
|
||||
let lemit = lookup_spectrum(le);
|
||||
Self {
|
||||
base,
|
||||
lemit,
|
||||
|
|
@ -286,4 +287,17 @@ impl LightFactory for InfiniteUniformLight {
|
|||
scene_radius: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut crate::utils::Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &crate::utils::ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
pub mod diffuse;
|
||||
pub mod distant;
|
||||
pub mod goniometric;
|
||||
pub mod infinite;
|
||||
pub mod point;
|
||||
pub mod projection;
|
||||
pub mod sampler;
|
||||
pub mod spot;
|
||||
|
|
|
|||
74
src/lights/point.rs
Normal file
74
src/lights/point.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
use crate::core::light::{CreateLight, LightBaseTrait, lookup_spectrum};
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::FloatTexture;
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||
use shared::PI;
|
||||
use shared::core::geometry::VectorLike;
|
||||
use shared::core::light::{Light, LightBase, LightType};
|
||||
use shared::core::medium::Medium;
|
||||
use shared::core::shape::Shape;
|
||||
use shared::lights::PointLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::Transform;
|
||||
|
||||
impl CreateLight for PointLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: shared::core::medium::MediumInterface,
|
||||
le: shared::core::spectrum::Spectrum,
|
||||
scale: shared::Float,
|
||||
_shape: Option<shared::utils::Ptr<Shape>>,
|
||||
_alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||
_image: Option<shared::utils::Ptr<Image>>,
|
||||
_image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||
_two_sided: Option<bool>,
|
||||
_fov: Option<shared::Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::DeltaPosition,
|
||||
render_from_light,
|
||||
medium_interface,
|
||||
);
|
||||
let i = lookup_spectrum(le);
|
||||
|
||||
Self { base, scale, i }
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_text: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
let l = parameters
|
||||
.get_one_spectrum(
|
||||
"L",
|
||||
colorspace.unwrap().illuminant,
|
||||
SpectrumType::Illuminant,
|
||||
)
|
||||
.unwrap();
|
||||
let lemit = lookup_spectrum(l);
|
||||
let mut scale = parameters.get_one_float("scale", 1);
|
||||
scale /= spectrum_to_photometric(l.unwrap());
|
||||
let phi_v = parameters.get_one_float("power", 1.);
|
||||
if phi_v > 0. {
|
||||
let k_e = 4. * PI;
|
||||
sc *= phi_v / k_e;
|
||||
}
|
||||
|
||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
||||
let tf = Transform::translate(from.into());
|
||||
let final_render = render_from_light * tf;
|
||||
let base = LightBase::new(LightType::DeltaPosition, render_from_light, medium.into());
|
||||
let specific = PointLight {
|
||||
base,
|
||||
scale,
|
||||
i: arena.alloc(lemit),
|
||||
};
|
||||
Light::Point(specific)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,30 @@
|
|||
use crate::core::image::ImageBuffer;
|
||||
use shared::core::light::LightBase;
|
||||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||
use crate::core::light::CreateLight;
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::spectra::colorspace::new;
|
||||
use crate::utils::{Ptr, Upload, resolve_filename};
|
||||
use shared::core::geometry::{Bounds2f, VectorLike};
|
||||
use shared::core::image::ImageAccess;
|
||||
use shared::core::light::{Light, LightBase};
|
||||
use shared::core::medium::MediumInterface;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::lights::ProjectionLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::math::{radians, square};
|
||||
use shared::utils::{Ptr, Transform};
|
||||
|
||||
pub struct ProjectionLightStorage {
|
||||
image: *const Image,
|
||||
distrib: *const PiecewiseConstant2D,
|
||||
image_color_space: *const RGBColorSpace,
|
||||
}
|
||||
|
||||
pub struct ProjectionLightHost {
|
||||
pub view: ProjectionLight,
|
||||
_storage: ProjectionLightStorage,
|
||||
}
|
||||
|
||||
impl ProjectionLightHost {
|
||||
pub fn new(
|
||||
impl CreateLight for ProjectionLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: MediumInterface,
|
||||
image: ImageBuffer,
|
||||
image_color_space: RGBColorSpace,
|
||||
scale: Float,
|
||||
fov: Float,
|
||||
le: Spectrum,
|
||||
scale: shared::Float,
|
||||
shape: Option<Ptr<Shape>>,
|
||||
alpha: Option<Ptr<FloatTexture>>,
|
||||
image: Option<Ptr<Image>>,
|
||||
image_color_space: Option<Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<shared::Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::DeltaPosition,
|
||||
|
|
@ -38,9 +42,9 @@ impl ProjectionLightHost {
|
|||
};
|
||||
|
||||
let hither = 1e-3;
|
||||
let screen_from_light = Transform::perspective(fov, hither, 1e30).unwrap();
|
||||
let screen_from_light = Transform::perspective(fov.unwrap(), hither, 1e30).unwrap();
|
||||
let light_from_screen = screen_from_light.inverse();
|
||||
let opposite = (radians(fov) / 2.).tan();
|
||||
let opposite = (radians(fov.unwrap()) / 2.).tan();
|
||||
let aspect_ratio = if aspect > 1. { aspect } else { 1. / aspect };
|
||||
let a = 4. * square(opposite) * aspect_ratio;
|
||||
let dwda = |p: Point2f| {
|
||||
|
|
@ -52,28 +56,109 @@ impl ProjectionLightHost {
|
|||
let d = image.get_sampling_distribution(dwda, screen_bounds);
|
||||
let distrib = PiecewiseConstant2D::new_with_bounds(&d, screen_bounds);
|
||||
|
||||
let storage = ProjectionLightStorage {
|
||||
Self {
|
||||
base,
|
||||
image,
|
||||
image_color_space,
|
||||
distrib,
|
||||
};
|
||||
|
||||
let view = ProjectionLight {
|
||||
base,
|
||||
image: storage.image,
|
||||
image_color_space: storage.image_color_space,
|
||||
screen_bounds,
|
||||
screen_from_light,
|
||||
light_from_screen,
|
||||
scale,
|
||||
hither,
|
||||
a,
|
||||
distrib: storage.distrib,
|
||||
};
|
||||
|
||||
Self {
|
||||
view,
|
||||
_storage: storage,
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut crate::utils::Arena,
|
||||
render_from_light: shared::utils::Transform,
|
||||
medium: Medium,
|
||||
parameters: &crate::utils::ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
_shape: &Shape,
|
||||
_alpha_text: &FloatTexture,
|
||||
_colorspace: Option<&shared::spectra::RGBColorSpace>,
|
||||
) -> Light {
|
||||
let mut scale = parameters.get_one_float("scale", 1.);
|
||||
let power = parameters.get_one_float("power", -1.);
|
||||
let fov = parameters.get_one_float("fov", 90.);
|
||||
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
||||
if filename.is_empty() {
|
||||
panic!(
|
||||
"{}: Must provide filename for projection light source.",
|
||||
loc
|
||||
);
|
||||
}
|
||||
let im = Image::read(filename, None).unwrap();
|
||||
let image: Image = im.image;
|
||||
let metadata: ImageMetadata = im.metadata;
|
||||
if image.has_any_infinite_pixels() {
|
||||
panic!(
|
||||
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||
loc, filename
|
||||
);
|
||||
}
|
||||
let colorspace: RGBColorSpace = metadata.colorspace.unwrap();
|
||||
let channel_desc = image
|
||||
.get_channel_desc(&["R", "G", "B"])
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"{:?}: Image '{}' must have R, G and B channels.",
|
||||
loc, filename
|
||||
)
|
||||
});
|
||||
let image = image.select_channels(channel_desc);
|
||||
scale /= spectrum_to_photometric(colorspace.illuminant);
|
||||
if power > 0. {
|
||||
let hither = 1e-3;
|
||||
let aspect = image.resolution().x() as Float / image.resolution().y() as Float;
|
||||
let screen_bounds = if aspect > 1. {
|
||||
Bounds2f::from_poins(Point2f::new(-aspect, -1.), Point2f::new(aspect, 1.))
|
||||
} else {
|
||||
Bounds2f::from_points(
|
||||
Point2f::new(-1., -1. / aspect),
|
||||
Point2f::new(1., 1. / aspect),
|
||||
)
|
||||
};
|
||||
let screen_from_light = Transform::perspective(fov, hither, 1e30);
|
||||
let light_from_screen = screen_from_light.inverse();
|
||||
let opposite = (radians(fov) / 2.).tan();
|
||||
let aspect_factor = if aspect > 1. { aspect } else { 1. / aspect };
|
||||
let a = 4. * square(aspect_factor);
|
||||
let mut sum = 0;
|
||||
let luminance = colorspace.luminance_vector();
|
||||
let res = image.resolution();
|
||||
for y in 0..res.y() {
|
||||
for x in 0..res.x() {
|
||||
let lerp_factor = Point2f::new((x + 0.5) / res.x(), (y + 0.5) / res.y());
|
||||
let ps = screen_bounds.lerp(lerp_factor);
|
||||
let w_point =
|
||||
light_from_screen.apply_to_point(Point3f::new(ps.x(), ps.y(), 0.));
|
||||
let w = Vector3f::from(w_point).normalize();
|
||||
let dwda = w.z().powi(3);
|
||||
for c in 0..3 {
|
||||
sum += image.get_channel(Point2f::new(x, y), c) * luminance[c] * dwda;
|
||||
}
|
||||
}
|
||||
}
|
||||
scale *= power / (a * sum / (res.x() + res.y()));
|
||||
}
|
||||
|
||||
let flip = Transform::scale(1., -1., 1.);
|
||||
let render_from_light_flip = render_from_light * flip;
|
||||
let specific = Self::new(
|
||||
render_from_light,
|
||||
medium_interface,
|
||||
le,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
image.upload(arena),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Light::Projection(specific)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
100
src/lights/spot.rs
Normal file
100
src/lights/spot.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||
use crate::core::light::CreateLight;
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::spectra::colorspace::new;
|
||||
use crate::utils::{Ptr, Upload, resolve_filename};
|
||||
use shared::PI;
|
||||
use shared::core::geometry::{Bounds2f, Frame, VectorLike};
|
||||
use shared::core::image::ImageAccess;
|
||||
use shared::core::light::{Light, LightBase, LightType};
|
||||
use shared::core::medium::MediumInterface;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::lights::{ProjectionLight, SpotLight};
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::math::{radians, square};
|
||||
use shared::utils::{Ptr, Transform};
|
||||
|
||||
impl CreateLight for SpotLight {
|
||||
fn new(
|
||||
render_from_light: Transform,
|
||||
medium_interface: MediumInterface,
|
||||
le: Spectrum,
|
||||
scale: shared::Float,
|
||||
shape: Option<Ptr<Shape>>,
|
||||
alpha: Option<Ptr<FloatTexture>>,
|
||||
image: Option<Ptr<Image>>,
|
||||
image_color_space: Option<Ptr<RGBColorSpace>>,
|
||||
two_sided: Option<bool>,
|
||||
fov: Option<shared::Float>,
|
||||
cos_falloff_start: Option<shared::Float>,
|
||||
total_width: Option<shared::Float>,
|
||||
) -> Self {
|
||||
let base = LightBase::new(
|
||||
LightType::DeltaPosition,
|
||||
render_from_light,
|
||||
MediumInterface::empty(),
|
||||
);
|
||||
|
||||
let iemit = lookup_spectrum(le);
|
||||
Self {
|
||||
base,
|
||||
iemit,
|
||||
scale,
|
||||
cos_falloff_end: radians(total_width.unwrap().cos()),
|
||||
cos_falloff_start: radians(cos_falloff_start.unwrap().cos()),
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
arena: &mut crate::utils::Arena,
|
||||
render_from_light: Transform,
|
||||
medium: Medium,
|
||||
parameters: &crate::utils::ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
shape: &Shape,
|
||||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
) -> Light {
|
||||
let i = parameters
|
||||
.get_one_spectrum(
|
||||
"I",
|
||||
colorspace.unwrap().illuminant,
|
||||
SpectrumType::Illuminant,
|
||||
)
|
||||
.expect("No spectrum");
|
||||
let mut scale = parameters.get_one_float("scale", 1.);
|
||||
let coneangle = parameters.get_one_float("coneangle", def);
|
||||
let conedelta = parameters.get_one_float("conedelta", def);
|
||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
||||
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
|
||||
let dir_to_z = Transform::from(Frame::from_z((to - from).normalize()));
|
||||
let t = Transform::translate(from.into()) * dir_to_z.inverse();
|
||||
let final_render = render_from_light * t;
|
||||
scale /= spectrum_to_photometric(i);
|
||||
|
||||
let phi_v = parameters.get_one_float("power", -1.);
|
||||
if phi_v > 0. {
|
||||
let cos_falloff_end = radians(coneangle).cos();
|
||||
let cos_falloff_start = radians(coneangle - conedelta).cos();
|
||||
let k_e =
|
||||
2. * PI * ((1. - cos_falloff_start) + (cos_falloff_start - cos_falloff_end) / 2);
|
||||
scale *= phi_v / k_e;
|
||||
}
|
||||
|
||||
let specific = SpotLight::new(
|
||||
final_render,
|
||||
medium.into(),
|
||||
le,
|
||||
scale,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(coneangle),
|
||||
Some(coneangle - conedelta),
|
||||
);
|
||||
}
|
||||
}
|
||||
259
src/utils/arena.rs
Normal file
259
src/utils/arena.rs
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
use crate::core::image::Image;
|
||||
use crate::core::texture::FloatTexture;
|
||||
use crate::utils::sampling::PiecewiseConstant2D;
|
||||
use core::alloc::Layout;
|
||||
use shared::Float;
|
||||
use shared::core::color::RGBToSpectrumTable;
|
||||
use shared::core::image::DeviceImage;
|
||||
use shared::core::shape::Shape;
|
||||
use shared::core::texture::GPUFloatTexture;
|
||||
use shared::spectra::{RGBColorSpace, StandardColorSpaces};
|
||||
use shared::textures::*;
|
||||
use shared::utils::Ptr;
|
||||
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
||||
|
||||
pub struct Arena {
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Arena {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffer: Vec::with_capacity(64 * 1024 * 1024),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc<T: Copy>(&mut self, value: T) -> Ptr<T> {
|
||||
let layout = Layout::new::<T>();
|
||||
let offset = self.alloc_raw(layout);
|
||||
|
||||
unsafe {
|
||||
let ptr = self.buffer.as_mut_ptr().add(offset) as *mut T;
|
||||
std::ptr::write(ptr, value);
|
||||
}
|
||||
|
||||
Ptr {
|
||||
offset: offset as i32,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc_slice<T: Copy>(&mut self, values: &[T]) -> Ptr<T> {
|
||||
let layout = Layout::array::<T>(values.len()).unwrap();
|
||||
let offset = self.alloc_raw(layout);
|
||||
|
||||
unsafe {
|
||||
let ptr = self.buffer.as_mut_ptr().add(offset) as *mut T;
|
||||
std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len());
|
||||
}
|
||||
|
||||
Ptr {
|
||||
offset: offset as i32,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_raw(&mut self, layout: Layout) -> usize {
|
||||
let len = self.buffer.len();
|
||||
let align_offset = (len + layout.align() - 1) & !(layout.align() - 1);
|
||||
let new_len = align_offset + layout.size();
|
||||
|
||||
if new_len > self.buffer.capacity() {
|
||||
// Growth strategy: Double capacity to reduce frequency of resizing
|
||||
let new_cap = std::cmp::max(self.buffer.capacity() * 2, new_len);
|
||||
self.buffer.reserve(new_cap - self.buffer.len());
|
||||
}
|
||||
|
||||
self.buffer.resize(new_len, 0);
|
||||
align_offset
|
||||
}
|
||||
|
||||
pub fn raw_data(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Upload {
|
||||
type Target: Copy;
|
||||
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target>;
|
||||
}
|
||||
|
||||
impl Upload for Shape {
|
||||
type Target = Shape;
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
arena.alloc(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for Image {
|
||||
type Target = DeviceImage;
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let pixels_ptr = arena.alloc_slice(&self.storage_as_slice());
|
||||
let device_img = DeviceImage {
|
||||
base: self.base,
|
||||
pixels: pixels_ptr,
|
||||
};
|
||||
arena.alloc(device_img)
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for FloatTexture {
|
||||
type Target = GPUFloatTexture;
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let gpu_variant = match self {
|
||||
FloatTexture::Constant(tex) => GPUFloatTexture::Constant(tex.clone()),
|
||||
FloatTexture::Checkerboard(tex) => GPUFloatTexture::Checkerboard(tex.clone()),
|
||||
FloatTexture::Dots(tex) => GPUFloatTexture::Dots(tex.clone()),
|
||||
FloatTexture::FBm(tex) => GPUFloatTexture::FBm(tex.clone()),
|
||||
FloatTexture::Windy(tex) => GPUFloatTexture::Windy(tex.clone()),
|
||||
FloatTexture::Wrinkled(tex) => GPUFloatTexture::Wrinkled(tex.clone()),
|
||||
FloatTexture::Constant(val) => GPUFloatTexture::Constant(*val),
|
||||
FloatTexture::Scaled(tex) => {
|
||||
let child_ptr = tex.texture.upload(arena);
|
||||
|
||||
let gpu_scaled = GPUFloatScaledTexture {
|
||||
tex: child_ptr,
|
||||
scale: tex.scale.upload(arena),
|
||||
};
|
||||
GPUFloatTexture::Scaled(gpu_scaled)
|
||||
}
|
||||
|
||||
FloatTexture::Mix(tex) => {
|
||||
let tex1_ptr = tex.tex1.upload(arena);
|
||||
let tex2_ptr = tex.tex2.upload(arena);
|
||||
let amount_ptr = tex.amount.upload(arena);
|
||||
|
||||
let gpu_mix = GPUFloatMixTexture {
|
||||
tex1: tex1_ptr,
|
||||
tex2: tex2_ptr,
|
||||
amount: amount_ptr,
|
||||
};
|
||||
GPUFloatTexture::Mix(gpu_mix)
|
||||
}
|
||||
|
||||
FloatTexture::DirectionMix(tex) => {
|
||||
let tex1_ptr = tex.tex1.upload(arena);
|
||||
let tex2_ptr = tex.tex2.upload(arena);
|
||||
let gpu_dmix = shared::textures::GPUFloatDirectionMixTexture {
|
||||
tex1: tex1_ptr,
|
||||
tex2: tex2_ptr,
|
||||
dir: tex.dir,
|
||||
};
|
||||
GPUFloatTexture::DirectionMix(gpu_dmix)
|
||||
}
|
||||
FloatTexture::Image(tex) => {
|
||||
let image_ptr = tex.image.upload(arena);
|
||||
|
||||
let gpu_image_tex = GPUFloatImageTexture {
|
||||
mapping: tex.mapping,
|
||||
tex_obj: image_ptr.offset as u64,
|
||||
scale: tex.scale,
|
||||
invert: tex.invert,
|
||||
mapping: tex.mapping,
|
||||
};
|
||||
GPUFloatTexture::Image(gpu_image_tex)
|
||||
}
|
||||
|
||||
FloatTexture::Ptex(tex) => {
|
||||
todo!("Implement Ptex buffer upload")
|
||||
}
|
||||
|
||||
FloatTexture::Bilerp(tex) => GPUFloatTexture::Bilerp(tex.clone()),
|
||||
};
|
||||
|
||||
arena.alloc(gpu_variant)
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for RGBToSpectrumTable {
|
||||
type Target = RGBToSpectrumTable;
|
||||
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let z_ptr = arena.alloc_slice(&self.z_nodes);
|
||||
let c_ptr = arena.alloc_slice(&self.coeffs);
|
||||
|
||||
let shared_table = RGBToSpectrumTable {
|
||||
z_nodes: z_ptr,
|
||||
coeffs: c_ptr,
|
||||
};
|
||||
|
||||
arena.alloc(shared_table)
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for RGBColorSpace {
|
||||
type Target = RGBColorSpace;
|
||||
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let table_ptr = self.rgb_to_spectrum_table.upload(arena);
|
||||
|
||||
let shared_space = RGBColorSpace {
|
||||
r: self.r,
|
||||
g: self.g,
|
||||
b: self.b,
|
||||
w: self.w,
|
||||
illuminant: self.illuminant.clone(),
|
||||
rgb_to_spectrum_table: table_ptr,
|
||||
xyz_from_rgb: self.xyz_from_rgb,
|
||||
rgb_from_xyz: self.rgb_from_xyz,
|
||||
};
|
||||
|
||||
arena.alloc(shared_space)
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for StandardColorSpaces {
|
||||
type Target = StandardColorSpaces;
|
||||
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let srgb_ptr = self.srgb.upload(arena);
|
||||
let dci_ptr = self.dci_p3.upload(arena);
|
||||
let rec_ptr = self.rec2020.upload(arena);
|
||||
let aces_ptr = self.aces2065_1.upload(arena);
|
||||
|
||||
let registry = StandardColorSpaces {
|
||||
srgb: srgb_ptr,
|
||||
dci_p3: dci_ptr,
|
||||
rec2020: rec_ptr,
|
||||
aces2065_1: aces_ptr,
|
||||
};
|
||||
|
||||
arena.alloc(registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl Upload for PiecewiseConstant2D {
|
||||
type Target = DevicePiecewiseConstant2D;
|
||||
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
let marginal_shared = self.p_marginal.to_shared(arena);
|
||||
|
||||
let conditionals_shared: Vec<DevicePiecewiseConstant1D> = self
|
||||
.p_conditionals
|
||||
.iter()
|
||||
.map(|c| c.to_shared(arena))
|
||||
.collect();
|
||||
|
||||
let conditionals_ptr = arena.alloc_slice(&conditionals_shared);
|
||||
|
||||
let shared_2d = DevicePiecewiseConstant2D {
|
||||
domain: self.domain,
|
||||
p_marginal: marginal_shared,
|
||||
n_conditionals: self.p_conditionals.len(),
|
||||
p_conditional_v: conditionals_ptr,
|
||||
};
|
||||
|
||||
arena.alloc(shared_2d)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Upload> Upload for Option<T> {
|
||||
type Target = T::Target;
|
||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||
match self {
|
||||
Some(val) => val.upload(arena),
|
||||
None => Ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue