Release Notes for v0.8.3


Even though this is a minor release (because 0.8.x was all about improving the performance) I did change the license to be more in line with the Rust ecosystem:

Licensed under either of

at your option.

I did ask my contributors (only two so far) about this license change and they were fine with it.

There is also a license.html shipping with the source code now, which can be best viewed in a browser (after cloning the repository), and lists the licenses of the crates rs-pbrt currently depends on.

I used cargo-about to create that file:

cd ~/git/github/rs_pbrt
cargo about generate about.hbs > license.html

Files Changed

98 files changed, 7120 insertions(+), 4768 deletions(-)


First of all the documentation style changed from:

old documentation style

to this (including a release number and a new color scheme):

new documentation style


Currently there are still some branches, but this release mainly introduced the arena branch and I hope to merge it for the next release.

The original C++ code uses Arena-Based Allocation in several places. I did ask questions about this topic on the Rust Users Forum before, but for that arena branch I decided that I just want to collect Bsdf and Bxdf in vectors and access them via an index:

impl SamplerIntegrator {
    pub fn render(&mut self, scene: &Scene, num_threads: u8) {
            crossbeam::scope(|scope| {
                let (pixel_tx, pixel_rx) = crossbeam_channel::bounded(num_cores);
                // spawn worker threads
                for _ in 0..num_cores {
                    let pixel_tx = pixel_tx.clone();
                    let mut tile_sampler: Box<Sampler> = sampler.clone_with_seed(0_u64);
                    scope.spawn(move |_| {
                        let mut arena_bsdf: Vec<Bsdf> = Vec::with_capacity(128);
                        let mut arena_bxdf: Vec<Bxdf> = Vec::with_capacity(128);
                        while let Some((x, y)) = {
                            for pixel in &tile_bounds {
                                while !done {
                                        l =
                                            &mut ray,
                                            &mut tile_sampler,
                                            &mut arena_bsdf,
                                            &mut arena_bxdf,
                                    done = !tile_sampler.start_next_sample();
                            // send the tile through the channel to main thread
                                .unwrap_or_else(|_| panic!("Failed to send tile"));
                // spawn thread to collect pixels and render image to file
                scope.spawn(move |_| {
                    for _ in pbr::PbIter::new( {
                        let film_tile = pixel_rx.recv().unwrap();
                        // merge image tile into _Film_
        film.write_image(1.0 as Float);

I spare you the details, but in theory the Bsdf and Bxdf instances are created while rendering, kept in both vectors, accessed via indices until the whole vector gets freed. The hope was to speed up the executable, but so far I did not decide if the performance is really improving that much for all integrators and related methods.


But there were two issues (fixed in both branches - master and arena):

  1. For Bidirectional Path Tracing (BDPT) the C++ version did render differently from the Rust version (see issue #127). This happened somewhere after release v0.7.3. I did pinpoint the commit introducing the bug and fixed it in both branches.

  2. A similar problem was never detected before regarding the Stochastic Progressive Photon Mapping (SPPM) algorithm. For the master branch I simply fixed the code showing up in the difference image between C++ and Rust (see issue #128). For the arena branch the code panicked at some point, so I had to fix parts of the code which uses the arena vectors, but indexed into them incorrectly.

The End

I hope I didn't forget anything important. Have fun and enjoy the v0.8.3 release.