This commit is contained in:
Wito Wiala 2026-02-18 19:53:13 +00:00
parent 45e866ebd1
commit 0b04d54346
19 changed files with 184 additions and 220 deletions

View file

@ -8,7 +8,7 @@ default = []
use_f64 = [] use_f64 = []
use_gpu = [] use_gpu = []
use_nvtx = [] use_nvtx = []
cuda = ["dep:cudarc"] cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"]
[dependencies] [dependencies]
anyhow = "1.0.100" anyhow = "1.0.100"
@ -36,6 +36,7 @@ ptex-filter = { path = "crates/ptex-filter" }
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 }
ptex = "0.3.0" ptex = "0.3.0"
# ptex-sys = "0.3.0" # ptex-sys = "0.3.0"
slice = "0.0.4" slice = "0.0.4"

View file

@ -366,6 +366,10 @@ impl SpectralFilm {
pub fn add_splat(&mut self, _p: Point2f, _v: SampledSpectrum, _lambda: &SampledWavelengths) { pub fn add_splat(&mut self, _p: Point2f, _v: SampledSpectrum, _lambda: &SampledWavelengths) {
todo!() todo!()
} }
pub fn get_pixel_rgb(&self, _p: Point2i, _splat_scale: Option<Float>) -> RGB {
todo!()
}
} }
#[repr(C)] #[repr(C)]

View file

@ -12,11 +12,11 @@ pub struct GPUFloatScaledTexture {
impl GPUFloatScaledTexture { impl GPUFloatScaledTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
let sc = self.scale.get().map(|t| t.evaluate(&ctx)).unwrap(); let sc = self.scale.get().map(|t| t.evaluate(ctx)).unwrap();
if sc == 0. { if sc == 0. {
return 0.; return 0.;
} }
self.tex.get().map(|t| t.evaluate(&ctx)).unwrap_or(0.0) * sc self.tex.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0) * sc
} }
} }
@ -33,11 +33,11 @@ impl GPUSpectrumScaledTexture {
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum {
let sc = self.scale.get().map(|t| t.evaluate(&ctx)).unwrap_or(0.); let sc = self.scale.get().map(|t| t.evaluate(ctx)).unwrap_or(0.);
self.tex self.tex
.get() .get()
.map(|t| t.evaluate(&ctx, &lambda)) .map(|t| t.evaluate(ctx, lambda))
.unwrap_or(SampledSpectrum::new(0.)) .unwrap_or(SampledSpectrum::new(0.))
* sc * sc
} }

View file

@ -1,7 +1,7 @@
use crate::core::image::ImageMetadata; use crate::core::image::ImageMetadata;
use crate::core::image::{Image, ImageIO}; use crate::core::image::{Image, ImageIO};
use crate::utils::read_float_file;
use crate::utils::{Arena, FileLoc, ParameterDictionary}; use crate::utils::{Arena, FileLoc, ParameterDictionary};
use crate::utils::{Upload, read_float_file};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use shared::Ptr; use shared::Ptr;
use shared::cameras::*; use shared::cameras::*;
@ -380,7 +380,7 @@ impl CameraFactory for Camera {
&lens_params, &lens_params,
focal_distance, focal_distance,
aperture_diameter, aperture_diameter,
aperture_image, aperture_image.upload(arena),
); );
arena.alloc(camera); arena.alloc(camera);

View file

@ -2,6 +2,7 @@ use crate::Arena;
use crate::core::image::{Image, ImageChannelDesc, ImageChannelValues, ImageIO, ImageMetadata}; use crate::core::image::{Image, ImageChannelDesc, ImageChannelValues, ImageIO, ImageMetadata};
use crate::films::*; use crate::films::*;
use crate::spectra::data::get_named_spectrum; use crate::spectra::data::get_named_spectrum;
use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use rayon::prelude::IntoParallelIterator; use rayon::prelude::IntoParallelIterator;
@ -12,7 +13,7 @@ use shared::core::filter::{Filter, FilterTrait};
use shared::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i}; use shared::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
use shared::core::image::PixelFormat; use shared::core::image::PixelFormat;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::spectra::{PiecewiseLinearSpectrum, RGBColorSpace, cie::SWATCHES_RAW}; use shared::spectra::{RGBColorSpace, cie::SWATCHES_RAW};
use shared::utils::math::{SquareMatrix, linear_least_squares}; use shared::utils::math::{SquareMatrix, linear_least_squares};
use shared::{Float, Ptr}; use shared::{Float, Ptr};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
@ -27,17 +28,26 @@ const N_SWATCH_REFLECTANCES: usize = 24;
const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLock::new(|| { const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLock::new(|| {
std::array::from_fn(|i| { std::array::from_fn(|i| {
let raw_data = SWATCHES_RAW[i]; let raw_data = SWATCHES_RAW[i];
let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false); let pls = PiecewiseLinearSpectrumBuffer::from_interleaved(raw_data, false);
Spectrum::Piecewise(pls) Spectrum::Piecewise(pls.device)
}) })
}); });
pub trait PixelSensorTrait: Sized { #[derive(Debug, Clone)]
fn get_swatches() -> Arc<[Spectrum; N_SWATCH_REFLECTANCES]> { struct SensorStorage {
Arc::new(*SWATCH_REFLECTANCES) r_bar: DenselySampledSpectrumBuffer,
} g_bar: DenselySampledSpectrumBuffer,
b_bar: DenselySampledSpectrumBuffer,
}
fn create( #[derive(Debug, Clone)]
pub struct PixelSensor {
device: DevicePixelSensor,
data: SensorStorage,
}
impl PixelSensor {
pub fn create(
params: &ParameterDictionary, params: &ParameterDictionary,
output_colorspace: Arc<RGBColorSpace>, output_colorspace: Arc<RGBColorSpace>,
exposure_time: Float, exposure_time: Float,
@ -103,23 +113,6 @@ pub trait PixelSensorTrait: Sized {
} }
} }
fn new(
r: &Spectrum,
g: &Spectrum,
b: &Spectrum,
output_colorspace: Arc<RGBColorSpace>,
sensor_illum: Option<&Spectrum>,
imaging_ratio: Float,
) -> Self;
fn new_with_white_balance(
output_colorspace: &RGBColorSpace,
sensor_illum: Option<&Spectrum>,
imaging_ratio: Float,
) -> Self;
}
impl PixelSensorTrait for DevicePixelSensor {
fn new( fn new(
r: &Spectrum, r: &Spectrum,
g: &Spectrum, g: &Spectrum,
@ -136,9 +129,9 @@ impl PixelSensorTrait for DevicePixelSensor {
None => &Spectrum::Dense(output_colorspace.as_ref().illuminant), None => &Spectrum::Dense(output_colorspace.as_ref().illuminant),
}; };
let r_bar = DenselySampledSpectrumBuffer::from_spectrum(r).device(); let r_bar = DenselySampledSpectrumBuffer::from_spectrum(r);
let g_bar = DenselySampledSpectrumBuffer::from_spectrum(g).device(); let g_bar = DenselySampledSpectrumBuffer::from_spectrum(g);
let b_bar = DenselySampledSpectrumBuffer::from_spectrum(b).device(); let b_bar = DenselySampledSpectrumBuffer::from_spectrum(b);
let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES]; let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES];
let swatches = Self::get_swatches(); let swatches = Self::get_swatches();
@ -147,9 +140,9 @@ impl PixelSensorTrait for DevicePixelSensor {
let rgb = DevicePixelSensor::project_reflectance::<RGB>( let rgb = DevicePixelSensor::project_reflectance::<RGB>(
&swatches[i], &swatches[i],
illum, illum,
&Spectrum::Dense(r_bar), &Spectrum::Dense(r_bar.device()),
&Spectrum::Dense(g_bar), &Spectrum::Dense(g_bar.device()),
&Spectrum::Dense(b_bar), &Spectrum::Dense(b_bar.device()),
); );
for c in 0..3 { for c in 0..3 {
rgb_camera[i][c] = rgb[c]; rgb_camera[i][c] = rgb[c];
@ -158,7 +151,7 @@ impl PixelSensorTrait for DevicePixelSensor {
let mut xyz_output = [[0.; 3]; N_SWATCH_REFLECTANCES]; let mut xyz_output = [[0.; 3]; N_SWATCH_REFLECTANCES];
let spectra = get_spectra_context(); let spectra = get_spectra_context();
let sensor_white_g = illum.inner_product(&Spectrum::Dense(g_bar.clone())); let sensor_white_g = illum.inner_product(&Spectrum::Dense(g_bar.device()));
let sensor_white_y = illum.inner_product(&Spectrum::Dense(spectra.y)); let sensor_white_y = illum.inner_product(&Spectrum::Dense(spectra.y));
for i in 0..N_SWATCH_REFLECTANCES { for i in 0..N_SWATCH_REFLECTANCES {
let s = swatches[i].clone(); let s = swatches[i].clone();
@ -177,13 +170,21 @@ impl PixelSensorTrait for DevicePixelSensor {
let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output) let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)
.expect("Could not convert sensor illuminance to XYZ space"); .expect("Could not convert sensor illuminance to XYZ space");
Self { let data = SensorStorage {
xyz_from_sensor_rgb, r_bar: r_bar.clone(),
r_bar, g_bar: g_bar.clone(),
g_bar, b_bar: b_bar.clone(),
b_bar, };
let device = DevicePixelSensor {
r_bar: r_bar.device(),
g_bar: g_bar.device(),
b_bar: b_bar.device(),
imaging_ratio, imaging_ratio,
} xyz_from_sensor_rgb,
};
Self { device, data }
} }
fn new_with_white_balance( fn new_with_white_balance(
@ -192,9 +193,9 @@ impl PixelSensorTrait for DevicePixelSensor {
imaging_ratio: Float, imaging_ratio: Float,
) -> Self { ) -> Self {
let spectra = get_spectra_context(); let spectra = get_spectra_context();
let r_bar = CIE_X_DATA.device(); let r_bar = CIE_X_DATA.clone();
let g_bar = CIE_Y_DATA.device(); let g_bar = CIE_Y_DATA.clone();
let b_bar = CIE_Z_DATA.device(); let b_bar = CIE_Z_DATA.clone();
let xyz_from_sensor_rgb: SquareMatrix<Float, 3>; let xyz_from_sensor_rgb: SquareMatrix<Float, 3>;
if let Some(illum) = sensor_illum { if let Some(illum) = sensor_illum {
@ -205,13 +206,29 @@ impl PixelSensorTrait for DevicePixelSensor {
xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default(); xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default();
} }
DevicePixelSensor { let data = SensorStorage {
r_bar: r_bar.clone(),
g_bar: g_bar.clone(),
b_bar: b_bar.clone(),
};
let device = DevicePixelSensor {
r_bar: r_bar.device(),
g_bar: g_bar.device(),
b_bar: b_bar.device(),
xyz_from_sensor_rgb, xyz_from_sensor_rgb,
r_bar,
g_bar,
b_bar,
imaging_ratio, imaging_ratio,
};
Self { data, device }
} }
pub fn device(&self) -> DevicePixelSensor {
self.device
}
fn get_swatches() -> Arc<[Spectrum; N_SWATCH_REFLECTANCES]> {
Arc::new(*SWATCH_REFLECTANCES)
} }
} }
@ -347,7 +364,7 @@ pub trait FilmTrait: Sync {
for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() { for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() {
let p_offset = Point2i::new(ix as i32, iy as i32); let p_offset = Point2i::new(ix as i32, iy as i32);
let values = ImageChannelValues::from(rgb_chunk); let values = ImageChannelValues::from(rgb_chunk);
image.set_channel(p_offset, &rgb_desc, &values); image.set_channels(p_offset, &values);
} }
} }

View file

@ -254,6 +254,12 @@ impl Image {
} }
// Read // Read
pub fn as_f32_slice(&self) -> Option<&[f32]> {
match &self.pixels {
PixelStorage::F32(data) => Some(data.as_slice()),
_ => None,
}
}
pub fn get_channel(&self, p: Point2i, c: i32) -> Float { pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into()) self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
} }
@ -336,6 +342,12 @@ impl Image {
} }
} }
pub fn set_channels(&mut self, p: Point2i, values: &ImageChannelValues) {
for i in 0..values.len() {
self.set_channel(p, i.try_into().unwrap(), values[i])
}
}
// Descriptions // Descriptions
pub fn get_channels_with_desc( pub fn get_channels_with_desc(
&self, &self,

View file

@ -426,7 +426,7 @@ impl ParserTarget for BasicSceneBuilder {
}) })
} }
fn world_begin(&mut self, loc: FileLoc, arena: &Arena) { fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) {
self.verify_options("WorldBegin", &loc); self.verify_options("WorldBegin", &loc);
self.current_block = BlockState::WorldBlock; self.current_block = BlockState::WorldBlock;
for i in 0..MAX_TRANSFORMS { for i in 0..MAX_TRANSFORMS {
@ -569,7 +569,7 @@ impl ParserTarget for BasicSceneBuilder {
}; };
let entity = TextureSceneEntity { let entity = TextureSceneEntity {
base, base,
render_from_object: self.graphics_state.ctm[0].clone(), render_from_object: AnimatedTransform::from_transform(&self.graphics_state.ctm[0]),
}; };
if type_name == "float" { if type_name == "float" {

View file

@ -109,7 +109,7 @@ impl BasicScene {
sampler: SceneEntity, sampler: SceneEntity,
integ: SceneEntity, integ: SceneEntity,
accel: SceneEntity, accel: SceneEntity,
arena: &Arena, arena: Arc<Arena>,
) { ) {
*self.integrator.lock() = Some(integ); *self.integrator.lock() = Some(integ);
*self.accelerator.lock() = Some(accel); *self.accelerator.lock() = Some(accel);
@ -131,7 +131,7 @@ impl BasicScene {
filt.expect("Must have a filter"), filt.expect("Must have a filter"),
Some(camera.camera_transform.clone()), Some(camera.camera_transform.clone()),
&film.loc, &film.loc,
arena, &arena,
) )
.expect("Must have a film"), .expect("Must have a film"),
); );
@ -141,14 +141,22 @@ impl BasicScene {
job: None, job: None,
}; };
let arena_sampler = Arc::clone(&arena);
let sampler_film = Arc::clone(&film_instance); let sampler_film = Arc::clone(&film_instance);
let sampler_job = run_async(move || { let sampler_job = run_async(move || {
let res = sampler_film.as_ref().base().full_resolution; let res = sampler_film.as_ref().base().full_resolution;
Sampler::create(&sampler.name, &sampler.parameters, res, &sampler.loc, arena) Sampler::create(
&sampler.name,
&sampler.parameters,
res,
&sampler.loc,
&arena_sampler,
)
.expect("Sampler was not correctly created") .expect("Sampler was not correctly created")
}); });
self.sampler_state.lock().job = Some(sampler_job); self.sampler_state.lock().job = Some(sampler_job);
let arena_camera = Arc::clone(&arena);
let camera_film = Arc::clone(&film_instance); let camera_film = Arc::clone(&film_instance);
let scene_ptr = Arc::clone(self); let scene_ptr = Arc::clone(self);
let camera_job = run_async(move || { let camera_job = run_async(move || {
@ -160,7 +168,7 @@ impl BasicScene {
*medium.unwrap(), *medium.unwrap(),
camera_film, camera_film,
&camera.base.loc, &camera.base.loc,
arena, &arena_camera,
) )
.expect("Failed to create camera") .expect("Failed to create camera")
}); });
@ -736,7 +744,7 @@ impl BasicScene {
if let Some(job) = state.jobs.remove(name) { if let Some(job) = state.jobs.remove(name) {
let job: AsyncJob<Medium> = job; let job: AsyncJob<Medium> = job;
let result: Medium = job.wait(); let result: Medium = job.wait();
let medium: Arc<Medium> = Arc::new(job.wait()); let medium: Arc<Medium> = Arc::new(result);
state.map.insert(name.to_string(), medium.clone()); state.map.insert(name.to_string(), medium.clone());
return Some(medium); return Some(medium);
} }

View file

@ -1,8 +1,8 @@
use super::*; use super::*;
use crate::core::film::{CreateFilmBase, PixelSensorTrait}; use crate::core::film::{CreateFilmBase, PixelSensor};
use crate::utils::containers::Array2D; use crate::utils::containers::Array2D;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use shared::core::film::{DevicePixelSensor, FilmBase, GBufferFilm}; use shared::core::film::{FilmBase, GBufferFilm};
use shared::core::filter::FilterTrait; use shared::core::filter::FilterTrait;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::AnimatedTransform; use shared::utils::AnimatedTransform;
@ -59,8 +59,8 @@ impl CreateFilm for GBufferFilm {
let colorspace = params.color_space.as_ref().unwrap(); let colorspace = params.color_space.as_ref().unwrap();
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY); let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
let write_fp16 = params.get_one_bool("savefp16", true); let write_fp16 = params.get_one_bool("savefp16", true);
let sensor = DevicePixelSensor::create(params, colorspace.clone(), exposure_time, loc)?; let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
let film_base = FilmBase::create(params, filter, Some(&sensor), loc); let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
let filename = params.get_one_string("filename", "pbrt.exr"); let filename = params.get_one_string("filename", "pbrt.exr");
if Path::new(&filename).extension() != Some("exr".as_ref()) { if Path::new(&filename).extension() != Some("exr".as_ref()) {

View file

@ -1,10 +1,10 @@
use super::*; use super::*;
use crate::Arena; use crate::Arena;
use crate::core::film::{CreateFilmBase, PixelSensorTrait}; use crate::core::film::{CreateFilmBase, PixelSensor};
use crate::utils::containers::Array2D; use crate::utils::containers::Array2D;
use anyhow::Result; use anyhow::Result;
use shared::core::camera::CameraTransform; use shared::core::camera::CameraTransform;
use shared::core::film::{DevicePixelSensor, Film, FilmBase, RGBFilm, RGBPixel}; use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel};
use shared::core::filter::FilterTrait; use shared::core::filter::FilterTrait;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
@ -72,8 +72,8 @@ impl CreateFilm for RGBFilm {
let colorspace = params.color_space.as_ref().unwrap(); let colorspace = params.color_space.as_ref().unwrap();
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY); let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
let write_fp16 = params.get_one_bool("savefp16", true); let write_fp16 = params.get_one_bool("savefp16", true);
let sensor = DevicePixelSensor::create(params, colorspace.clone(), exposure_time, loc)?; let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
let film_base = FilmBase::create(params, filter, Some(&sensor), loc); let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16); let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16);
Ok(Film::RGB(film.device)) Ok(Film::RGB(film.device))
} }

View file

@ -1,11 +1,11 @@
use super::*; use super::*;
use crate::core::film::{CreateFilmBase, PixelSensorTrait}; use crate::core::film::{CreateFilmBase, PixelSensor};
use crate::utils::containers::Array2D; use crate::utils::containers::Array2D;
use crate::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use shared::Float; use shared::Float;
use shared::core::camera::CameraTransform; use shared::core::camera::CameraTransform;
use shared::core::film::{DevicePixelSensor, FilmBase, SpectralFilm, SpectralPixel}; use shared::core::film::{FilmBase, SpectralFilm, SpectralPixel};
use shared::core::filter::FilterTrait; use shared::core::filter::FilterTrait;
use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN, RGBColorSpace}; use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN, RGBColorSpace};
use shared::utils::AtomicFloat; use shared::utils::AtomicFloat;
@ -99,8 +99,8 @@ impl CreateFilm for SpectralFilm {
let colorspace = params.color_space.as_ref().unwrap(); let colorspace = params.color_space.as_ref().unwrap();
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY); let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
let write_fp16 = params.get_one_bool("savefp16", true); let write_fp16 = params.get_one_bool("savefp16", true);
let sensor = DevicePixelSensor::create(params, colorspace.clone(), exposure_time, loc)?; let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
let film_base = FilmBase::create(params, filter, Some(&sensor), loc); let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
let filename = params.get_one_string("filename", "pbrt.exr"); let filename = params.get_one_string("filename", "pbrt.exr");
if Path::new(&filename).extension() != Some("exr".as_ref()) { if Path::new(&filename).extension() != Some("exr".as_ref()) {

View file

@ -1,6 +1,8 @@
mod context; pub mod context;
pub use context::{GpuState, gpu_init, gpu_state, gpu_state_or_panic, gpu_thread_init}; pub use context::{
GPU_STATE, GpuContext, GpuState, gpu_init, gpu_state, gpu_state_or_panic, gpu_thread_init,
};
pub mod wavefront; pub mod wavefront;
@ -15,5 +17,5 @@ pub enum GpuError {
} }
pub fn gpu_unwrap() -> &'static GpuContext { pub fn gpu_unwrap() -> &'static GpuContext {
context::GPU_CONTEXT.get().expect("GPU not initialized") context::GPU_STATE.get().expect("GPU not initialized")
} }

View file

@ -18,13 +18,14 @@ use shared::spectra::RGBColorSpace;
use shared::utils::math::{radians, square}; use shared::utils::math::{radians, square};
use shared::utils::{Ptr, Transform}; use shared::utils::{Ptr, Transform};
use std::path::Path; use std::path::Path;
use std::sync::Arc;
pub trait CreateProjectionLight { pub trait CreateProjectionLight {
fn new( fn new(
render_from_light: Transform, render_from_light: Transform,
medium_interface: MediumInterface, medium_interface: MediumInterface,
scale: Float, scale: Float,
image: Ptr<Image>, image: Arc<Image>,
image_color_space: Ptr<RGBColorSpace>, image_color_space: Ptr<RGBColorSpace>,
fov: Float, fov: Float,
) -> Self; ) -> Self;
@ -35,7 +36,7 @@ impl CreateProjectionLight for ProjectionLight {
render_from_light: Transform, render_from_light: Transform,
medium_interface: MediumInterface, medium_interface: MediumInterface,
scale: Float, scale: Float,
image: Ptr<Image>, image: Arc<Image>,
image_color_space: Ptr<RGBColorSpace>, image_color_space: Ptr<RGBColorSpace>,
fov: Float, fov: Float,
) -> Self { ) -> Self {
@ -158,7 +159,7 @@ impl CreateLight for ProjectionLight {
render_from_light_flip, render_from_light_flip,
medium.into(), medium.into(),
scale, scale,
image.upload(arena), Arc::new(image),
colorspace.upload(arena), colorspace.upload(arena),
fov, fov,
); );

View file

@ -22,7 +22,7 @@ pub struct TriQuadMesh {
} }
#[derive(Debug)] #[derive(Debug)]
struct TriangleMeshStorage { pub(crate) struct TriangleMeshStorage {
pub p: Vec<Point3f>, pub p: Vec<Point3f>,
pub n: Vec<Normal3f>, pub n: Vec<Normal3f>,
pub s: Vec<Vector3f>, pub s: Vec<Vector3f>,
@ -32,7 +32,7 @@ struct TriangleMeshStorage {
} }
#[derive(Debug)] #[derive(Debug)]
struct BilinearMeshStorage { pub(crate) struct BilinearMeshStorage {
pub vertex_indices: Vec<i32>, pub vertex_indices: Vec<i32>,
pub p: Vec<Point3f>, pub p: Vec<Point3f>,
pub n: Vec<Normal3f>, pub n: Vec<Normal3f>,

View file

@ -6,7 +6,6 @@ mod fbm;
mod image; mod image;
mod marble; mod marble;
mod mix; mod mix;
mod ptex;
mod scaled; mod scaled;
mod windy; mod windy;
mod wrinkled; mod wrinkled;

View file

@ -1,74 +0,0 @@
// use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait};
use crate::spectra::get_colorspace_context;
use shared::core::spectrum::SpectrumTrait;
// use ptex::Cache;
// use ptex_filter::{PtexFilter, PtexFilterOptions, PtexFilterType};
use shared::Float;
use shared::core::color::{ColorEncoding, RGB};
use shared::core::texture::{SpectrumType, TextureEvalContext};
use shared::spectra::{RGBIlluminantSpectrum, SampledSpectrum, SampledWavelengths};
#[derive(Debug, Clone)]
pub struct PtexTextureBase {
pub valid: bool,
pub filename: String,
pub encoding: ColorEncoding,
pub scale: Float,
}
impl PtexTextureBase {
pub fn new(filename: String, encoding: ColorEncoding, scale: Float) -> Self {
log::warn!(
"Ptex support is currently disabled. Texture '{}' will render as Magenta.",
filename
);
Self {
filename,
encoding,
scale,
valid: false,
}
}
pub fn sample_texture(&self, _ctx: &TextureEvalContext) -> Option<RGB> {
Some(RGB::new(1.0, 0.0, 1.0) * self.scale)
}
}
#[derive(Debug, Clone)]
pub struct FloatPtexTexture {
pub base: PtexTextureBase,
}
impl FloatTextureTrait for FloatPtexTexture {
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
if let Some(rgb) = self.base.sample_texture(ctx) {
rgb.g
} else {
0.0
}
}
}
#[derive(Clone, Debug)]
pub struct SpectrumPtexTexture {
pub base: PtexTextureBase,
pub spectrum_type: SpectrumType,
}
impl SpectrumTextureTrait for SpectrumPtexTexture {
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
if let Some(rgb) = self.base.sample_texture(ctx) {
let stdcs = get_colorspace_context();
let srgb = stdcs.srgb.as_ref();
RGBIlluminantSpectrum::new(srgb, rgb).sample(lambda)
} else {
SampledSpectrum::new(0.0)
}
}
}
pub trait CreateGPUPtexTexture {}

View file

@ -78,23 +78,27 @@ impl Arena {
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
unsafe fn alloc_unified(&self, layout: Layout) -> *mut u8 { unsafe fn alloc_unified(&self, layout: Layout) -> *mut u8 {
use cuda_runtime_sys::*; use cust::memory::{UnifiedPointer, cuda_free_unified, cuda_malloc_unified};
let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let size = layout.size().max(layout.align()); let size = layout.size().max(layout.align());
if size == 0 {
let result = unsafe { cudaMallocManaged(&mut ptr, size, cudaMemAttachGlobal) }; return layout.align() as *mut u8;
if result != cudaError::cudaSuccess {
panic!("cudaMallocManaged failed: {:?}", result);
} }
self.buffer.push((ptr as *mut u8, layout)); let mut unified_ptr =
ptr as *mut u8 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"))] #[cfg(not(feature = "cuda"))]
unsafe fn alloc_unified(&self, layout: Layout) -> *mut u8 { 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 ptr = unsafe { std::alloc::alloc(layout) };
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.buffer.push((ptr, layout)); inner.buffer.push((ptr, layout));
@ -129,20 +133,23 @@ impl Arena {
// 5. Return handle // 5. Return handle
0 0
} }
// pub fn raw_data(&self) -> &[u8] {
// &self.buffer
// }
} }
impl Drop for Arena { 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(..) {
if layout.size() == 0 {
continue;
}
unsafe { unsafe {
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
{ {
cuda_runtime_sys::cudaFree(ptr as *mut _); 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"))] #[cfg(not(feature = "cuda"))]
{ {

View file

@ -5,10 +5,12 @@ use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike};
use shared::core::image::{WrapMode, WrapMode2D}; use shared::core::image::{WrapMode, WrapMode2D};
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::math::{lerp, safe_sqrt, square}; use shared::utils::math::{lerp, safe_sqrt, square};
use std::path::Path;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::{Add, Mul, Sub}; use std::ops::{Add, Mul, Sub};
use std::path::Path;
#[cfg(feature = "cuda")]
use std::sync::OnceLock;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -59,7 +61,6 @@ impl Eq for MIPMapFilterOptions {}
impl Hash for MIPMapFilterOptions { impl Hash for MIPMapFilterOptions {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.filter.hash(state); self.filter.hash(state);
// Hash the bits, not the float value
self.max_anisotropy.to_bits().hash(state); self.max_anisotropy.to_bits().hash(state);
} }
} }
@ -76,11 +77,9 @@ impl MIPMapSample for Float {
fn zero() -> Self { fn zero() -> Self {
0. 0.
} }
fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self { fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self {
image.bilerp_channel_with_wrap(st, 0, wrap) image.bilerp_channel_with_wrap(st, 0, wrap)
} }
fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self { fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self {
image.get_channel_with_wrap(st, 0, wrap) image.get_channel_with_wrap(st, 0, wrap)
} }
@ -90,7 +89,6 @@ impl MIPMapSample for RGB {
fn zero() -> Self { fn zero() -> Self {
RGB::new(0., 0., 0.) RGB::new(0., 0., 0.)
} }
fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self { fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self {
let nc = image.n_channels(); let nc = image.n_channels();
if nc >= 3 { if nc >= 3 {
@ -103,7 +101,6 @@ impl MIPMapSample for RGB {
RGB::new(v, v, v) RGB::new(v, v, v)
} }
} }
fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self { fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self {
let nc = image.n_channels(); let nc = image.n_channels();
if nc >= 3 { if nc >= 3 {
@ -124,6 +121,8 @@ pub struct MIPMap {
pub color_space: Option<RGBColorSpace>, pub color_space: Option<RGBColorSpace>,
pub wrap_mode: WrapMode, pub wrap_mode: WrapMode,
pub options: MIPMapFilterOptions, pub options: MIPMapFilterOptions,
#[cfg(feature = "cuda")]
tex_obj: OnceLock<u64>,
} }
impl MIPMap { impl MIPMap {
@ -139,6 +138,8 @@ impl MIPMap {
color_space, color_space,
wrap_mode, wrap_mode,
options, options,
#[cfg(feature = "cuda")]
tex_obj: OnceLock::new(),
} }
} }
@ -174,7 +175,6 @@ impl MIPMap {
mut dst1: Vector2f, mut dst1: Vector2f,
) -> T { ) -> T {
if self.options.filter != FilterFunction::Ewa { if self.options.filter != FilterFunction::Ewa {
// Compute largest change in texture coordinates
let width = 2.0 let width = 2.0
* [ * [
dst0.x().abs(), dst0.x().abs(),
@ -186,8 +186,6 @@ impl MIPMap {
.reduce(Float::max) .reduce(Float::max)
.unwrap_or(0.0); .unwrap_or(0.0);
// Compute MIP Map level
// n_levels - 1 + log2(width) maps width=1.0 to the top level (1x1)
let n_levels = self.levels() as Float; let n_levels = self.levels() as Float;
let level = n_levels - 1.0 + width.max(1e-8).log2(); let level = n_levels - 1.0 + width.max(1e-8).log2();
@ -196,7 +194,6 @@ impl MIPMap {
} }
let i_level = level.floor() as usize; let i_level = level.floor() as usize;
return match self.options.filter { return match self.options.filter {
FilterFunction::Point => { FilterFunction::Point => {
let resolution = self.level_resolution(i_level); let resolution = self.level_resolution(i_level);
@ -208,7 +205,6 @@ impl MIPMap {
} }
FilterFunction::Bilinear => self.bilerp(i_level, st), FilterFunction::Bilinear => self.bilerp(i_level, st),
FilterFunction::Trilinear => { FilterFunction::Trilinear => {
// Interpolate between current level and next level
let v0 = self.bilerp(i_level, st); let v0 = self.bilerp(i_level, st);
let v1 = self.bilerp(i_level + 1, st); let v1 = self.bilerp(i_level + 1, st);
let t = level - i_level as Float; let t = level - i_level as Float;
@ -221,12 +217,9 @@ impl MIPMap {
if dst0.norm_squared() < dst1.norm_squared() { if dst0.norm_squared() < dst1.norm_squared() {
std::mem::swap(&mut dst0, &mut dst1); std::mem::swap(&mut dst0, &mut dst1);
} }
let longer_len = dst0.norm(); let longer_len = dst0.norm();
let mut shorter_len = dst1.norm(); let mut shorter_len = dst1.norm();
// If ellipse is too thin, fatten the minor axis to limit the number
// of texels
if shorter_len * self.options.max_anisotropy < longer_len && shorter_len > 0.0 { if shorter_len * self.options.max_anisotropy < longer_len && shorter_len > 0.0 {
let scale = longer_len / (shorter_len * self.options.max_anisotropy); let scale = longer_len / (shorter_len * self.options.max_anisotropy);
dst1 *= scale; dst1 *= scale;
@ -242,7 +235,6 @@ impl MIPMap {
let v0 = self.ewa(ilod, st, dst0, dst1); let v0 = self.ewa(ilod, st, dst0, dst1);
let v1 = self.ewa(ilod + 1, st, dst0, dst1); let v1 = self.ewa(ilod + 1, st, dst0, dst1);
lerp(lod - ilod as Float, v0, v1) lerp(lod - ilod as Float, v0, v1)
} }
@ -250,12 +242,10 @@ impl MIPMap {
if level >= self.levels() { if level >= self.levels() {
panic!("MIPMap level out of bounds"); panic!("MIPMap level out of bounds");
} }
let image = &self.pyramid[level]; let image = &self.pyramid[level];
let wrap_2d = WrapMode2D { let wrap_2d = WrapMode2D {
uv: [self.wrap_mode; 2], uv: [self.wrap_mode; 2],
}; };
T::sample_texel(image, st, wrap_2d) T::sample_texel(image, st, wrap_2d)
} }
@ -277,6 +267,7 @@ impl MIPMap {
if level > self.levels() { if level > self.levels() {
return self.texel(self.levels() - 1, Point2i::new(0, 0)); return self.texel(self.levels() - 1, Point2i::new(0, 0));
} }
let level_res = self.level_resolution(level); let level_res = self.level_resolution(level);
st[0] = st[0] * level_res[0] as Float - 0.5; st[0] = st[0] * level_res[0] as Float - 0.5;
st[1] = st[1] * level_res[1] as Float - 0.5; st[1] = st[1] * level_res[1] as Float - 0.5;
@ -284,6 +275,7 @@ impl MIPMap {
dst0[1] *= level_res[1] as Float; dst0[1] *= level_res[1] as Float;
dst1[0] *= level_res[0] as Float; dst1[0] *= level_res[0] as Float;
dst1[1] *= level_res[1] as Float; dst1[1] *= level_res[1] as Float;
let mut a = square(dst0[1]) + square(dst1[1]) + 1.; let mut a = square(dst0[1]) + square(dst1[1]) + 1.;
let mut b = -2. * (dst0[0] + dst0[1] + dst1[1]); let mut b = -2. * (dst0[0] + dst0[1] + dst1[1]);
let mut c = square(dst0[0]) + square(dst1[0]) + 1.; let mut c = square(dst0[0]) + square(dst1[0]) + 1.;
@ -296,35 +288,30 @@ impl MIPMap {
let inv_det = 1. / det; let inv_det = 1. / det;
let u_sqrt = safe_sqrt(det * c); let u_sqrt = safe_sqrt(det * c);
let v_sqrt = safe_sqrt(det * a); let v_sqrt = safe_sqrt(det * a);
let s0: i32 = (st[0] - 2. * inv_det * u_sqrt).ceil() as i32; let s0: i32 = (st[0] - 2. * inv_det * u_sqrt).ceil() as i32;
let s1: i32 = (st[0] + 2. * inv_det * u_sqrt).floor() as i32; let s1: i32 = (st[0] + 2. * inv_det * u_sqrt).floor() as i32;
let t0: i32 = (st[1] - 2. * inv_det * v_sqrt).ceil() as i32; let t0: i32 = (st[1] - 2. * inv_det * v_sqrt).ceil() as i32;
let t1: i32 = (st[1] + 2. * inv_det * v_sqrt).floor() as i32; let t1: i32 = (st[1] + 2. * inv_det * v_sqrt).floor() as i32;
let mut sum = T::zero(); let mut sum = T::zero();
let mut sum_wts = 0.; let mut sum_wts = 0.;
for it in t0..=t1 { for it in t0..=t1 {
let tt = it as Float - st[1]; let tt = it as Float - st[1];
for is in s0..=s1 { for is in s0..=s1 {
let ss = is as Float - st[0]; let ss = is as Float - st[0];
// Compute squared radius and filter texel if inside ellipse
let r2 = a * square(ss) + b * ss * tt + c * square(tt); let r2 = a * square(ss) + b * ss * tt + c * square(tt);
if r2 < 1.0 { if r2 < 1.0 {
// Map r2 to LUT index
let index = (r2 * MIP_FILTER_LUT_SIZE as Float) let index = (r2 * MIP_FILTER_LUT_SIZE as Float)
.min((MIP_FILTER_LUT_SIZE - 1) as Float) .min((MIP_FILTER_LUT_SIZE - 1) as Float)
as usize; as usize;
let weight = MIP_FILTER_LUT[index]; let weight = MIP_FILTER_LUT[index];
// Accumulate
sum = sum + self.texel::<T>(level, Point2i::new(is, it)) * weight; sum = sum + self.texel::<T>(level, Point2i::new(is, it)) * weight;
sum_wts += weight; sum_wts += weight;
} }
} }
} }
sum * (1. / sum_wts) sum * (1. / sum_wts)
} }
@ -336,9 +323,6 @@ impl MIPMap {
) -> Result<MIPMap, ()> { ) -> Result<MIPMap, ()> {
let image_and_metadata = Image::read(filename, Some(encoding)).unwrap(); let image_and_metadata = Image::read(filename, Some(encoding)).unwrap();
let image = image_and_metadata.image; let image = image_and_metadata.image;
// if image.n_channels() != 1 {
// let rgba_dsc = image.all_channels_desc();
// }
Ok(MIPMap::new( Ok(MIPMap::new(
image, image,
image_and_metadata.metadata.colorspace, image_and_metadata.metadata.colorspace,
@ -369,7 +353,7 @@ fn create_cuda_texture(pyramid: &[Image], wrap_mode: WrapMode) -> u64 {
base.resolution().x() as usize, base.resolution().x() as usize,
base.resolution().y() as usize, base.resolution().y() as usize,
); );
let channels = base.n_channels(); let channels = base.n_channels() as usize;
unsafe { unsafe {
let channel_desc = cudaCreateChannelDesc( let channel_desc = cudaCreateChannelDesc(
@ -377,57 +361,60 @@ fn create_cuda_texture(pyramid: &[Image], wrap_mode: WrapMode) -> u64 {
if channels > 1 { 32 } else { 0 }, if channels > 1 { 32 } else { 0 },
if channels > 2 { 32 } else { 0 }, if channels > 2 { 32 } else { 0 },
if channels > 3 { 32 } else { 0 }, if channels > 3 { 32 } else { 0 },
cudaChannelFormatKindFloat, cudaChannelFormatKind::cudaChannelFormatKindFloat,
); );
let mut array: cudaArray_t = std::ptr::null_mut(); let mut array: cudaArray_t = std::ptr::null_mut();
cudaMallocArray(&mut array, &channel_desc, width, height, 0); cudaMallocArray(&mut array, &channel_desc, width, height, 0);
let pixels = base.as_slice(); // Get raw pixel data from the base image
let pixel_data = base
.as_f32_slice()
.expect("GPU upload requires Float encoded image");
let row_bytes = width * channels * std::mem::size_of::<f32>();
cudaMemcpy2DToArray( cudaMemcpy2DToArray(
array, array,
0, 0,
0, 0,
pixels.as_ptr() as *const _, pixel_data.as_ptr() as *const _,
width * channels * std::mem::size_of::<f32>(), row_bytes,
width * channels * std::mem::size_of::<f32>(), row_bytes,
height, height,
cudaMemcpyHostToDevice, cudaMemcpyKind::cudaMemcpyHostToDevice,
); );
// 4. Create texture object
let res_desc = cudaResourceDesc { let res_desc = cudaResourceDesc {
resType: cudaResourceTypeArray, resType: cudaResourceType::cudaResourceTypeArray,
res: cudaResourceDesc__bindgen_ty_1 { res: cudaResourceDesc__bindgen_ty_1 {
array: cudaResourceDesc__bindgen_ty_1__bindgen_ty_1 { array }, array: cudaResourceDesc__bindgen_ty_1__bindgen_ty_1 { array },
}, },
}; };
let address_mode = match wrap_mode { let address_mode = match wrap_mode {
WrapMode::Repeat => cudaAddressModeWrap, WrapMode::Repeat => cudaTextureAddressMode::cudaAddressModeWrap,
WrapMode::Clamp => cudaAddressModeClamp, WrapMode::Clamp => cudaTextureAddressMode::cudaAddressModeClamp,
WrapMode::Black => cudaAddressModeBorder, WrapMode::Black => cudaTextureAddressMode::cudaAddressModeBorder,
WrapMode::OctahedralSphere => cudaAddressModeBorder, WrapMode::OctahedralSphere => cudaTextureAddressMode::cudaAddressModeBorder,
}; };
let tex_desc = cudaTextureDesc { let tex_desc = cudaTextureDesc {
addressMode: [address_mode; 3], addressMode: [address_mode; 3],
filterMode: cudaFilterModeLinear, filterMode: cudaTextureFilterMode::cudaFilterModeLinear,
readMode: cudaReadModeElementType, readMode: cudaTextureReadMode::cudaReadModeElementType,
normalizedCoords: 1, normalizedCoords: 1,
..std::mem::zeroed() ..std::mem::zeroed()
}; };
let mut tex_obj: cudaTextureObject_t = 0; let mut tex_obj: cudaTextureObject_t = 0;
cudaCreateTextureObject(&mut tex_obj, &res_desc, &tex_desc, std::ptr::null()); cudaCreateTextureObject(&mut tex_obj, &res_desc, &tex_desc, std::ptr::null());
tex_obj tex_obj
} }
} }
static MIP_FILTER_LUT_SIZE: usize = 128; static MIP_FILTER_LUT_SIZE: usize = 128;
static MIP_FILTER_LUT: [Float; MIP_FILTER_LUT_SIZE] = [ static MIP_FILTER_LUT: [Float; MIP_FILTER_LUT_SIZE] = [
// MIPMap EWA Lookup Table Values
0.864664733, 0.864664733,
0.849040031, 0.849040031,
0.83365953, 0.83365953,

View file

@ -50,7 +50,7 @@ pub trait ParserTarget {
fn medium_interface(&mut self, inside_name: &str, outside_name: &str, loc: FileLoc); fn medium_interface(&mut self, inside_name: &str, outside_name: &str, loc: FileLoc);
fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc); fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
fn world_begin(&mut self, loc: FileLoc, arena: &Arena); fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>);
fn attribute_begin(&mut self, loc: FileLoc); fn attribute_begin(&mut self, loc: FileLoc);
fn attribute_end(&mut self, loc: FileLoc); fn attribute_end(&mut self, loc: FileLoc);
fn attribute(&mut self, target: &str, params: ParsedParameterVector, loc: FileLoc); fn attribute(&mut self, target: &str, params: ParsedParameterVector, loc: FileLoc);
@ -459,7 +459,7 @@ impl ParserTarget for FormattingParserTarget {
println!("{}CoordSysTransform \"{}\"", self.indent(0), name); println!("{}CoordSysTransform \"{}\"", self.indent(0), name);
} }
fn world_begin(&mut self, _loc: FileLoc, _arena: &Arena) { fn world_begin(&mut self, _loc: FileLoc, _arena: Arc<Arena>) {
println!("{}WorldBegin", self.indent(0)); println!("{}WorldBegin", self.indent(0));
self.cat_indent_count += 4; self.cat_indent_count += 4;
} }
@ -1030,7 +1030,7 @@ impl<'a> SceneParser<'a> {
}, },
'W' => match token.text.as_str() { 'W' => match token.text.as_str() {
"WorldBegin" => self.target.world_begin(token.loc, &arena), "WorldBegin" => self.target.world_begin(token.loc, arena.clone()),
"WorldEnd" => {} "WorldEnd" => {}
_ => { _ => {
return Err(ParserError::Generic( return Err(ParserError::Generic(