PBRT and Tev

Jan Walter September 05, 2023 [TRACE] #rs_pbrt #pbrt-v4 #pbrt-v3 #tev

iXialumy discussed (a while ago) with me how to connect rs-pbrt to the image viewer tev (while rendering) and made a pull request about it, pretty much the same way pbrt-v4 does it (see video). For a long time I had not much time to spend on rs_pbrt and only updated the dependencies and made minor code changes. Yesterday I finally took the time to look at his solution and created a tev branch on the main repository on sourcehut (the github and codeberg repositories are more or less just mirrors with their own capability to create issues and pull requests). In contrast to pbrt-v4 the current solution renders still in buckets (which means the part of the image gets visible in tev only once all samples for those pixels are calculated), whereas pbrt-v4 renders a full resolution noisy image first and converges to a less noisy image while more and more samples are computed:

rs_pbrt connecting to tev

You should be able to clone the repository (read-only) either by:

$ git clone https://git.sr.ht/~wahn/rs-pbrt
Cloning into 'rs-pbrt'...
...

Or (a read/write version) by:

$ git clone git@git.sr.ht:~wahn/rs-pbrt
Cloning into 'rs-pbrt'...
...

To use the tev branch you must figure out that there is a remote branch of that name and then locally checkout that branch.

$ git branch
* master
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/tev
$ git checkout tev
branch 'tev' set up to track 'origin/tev'.
Switched to a new branch 'tev'
$ git branch
  master
* tev

If you want to compile two versions (one for the tev branch and one for the master branch) and compile the version information into the executables you can do the following:

$ git describe --tags
v0.9.10-1-gccda035
$ export GIT_DESCRIBE="v0.9.10-1-gccda035"
$ make
...
$ mv target/ target_tev
$ ./target_tev/release/rs_pbrt -h | head -n 1
rs_pbrt version 0.9.10 (v0.9.10-1-gccda035) [Detected 28 cores]

And the same for the master branch:

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ git branch
* master
  tev
$ git describe --tags
v0.9.10
$ export GIT_DESCRIBE="v0.9.10"
$ make
...
$ mv target/ target_master
$ ./target_master/release/rs_pbrt -h | head -n 1
rs_pbrt version 0.9.10 (v0.9.10) [Detected 28 cores]

If you pipe the output of rs_pbrt -h into two text files and compare them, you will see that there is a new option now to connect to the display server (or better tev):

$ diff -b rs_pbrt_tev.txt rs_pbrt_master.txt
1c1
< rs_pbrt version 0.9.10 (v0.9.10-1-gccda035) [Detected 28 cores]
---
> rs_pbrt version 0.9.10 (v0.9.10) [Detected 28 cores]
16d15
<       --display-server <DISPLAY_SERVER>  The address and port of the display server

The full command used in the screenshot above was:

$ time /media/datadisk3/git/sourcehut/rs-pbrt/target_tev/release/rs_pbrt --display-server localhost:14158 --path ganesha.pbrt 
...
real	1m32,956s
user	35m11,823s
sys	0m3,643s

The C++ version of pbrt-v3 should be slightly faster than the Rust version:

$ pwd
/media/datadisk3/Graphics/Rendering/PBRT/pbrt-v3-scenes/ganesha
$ time /media/datadisk3/git/github/pbrt-v3/build/pbrt ganesha.pbrt 
pbrt version 3 (built Sep  5 2023 at 12:37:06) [Detected 28 cores]
Copyright (c)1998-2018 Matt Pharr, Greg Humphreys, and Wenzel Jakob.
The source code to pbrt (but *not* the book contents) is covered by the BSD License.
See the file LICENSE.txt for the conditions of the license.
Rendering: [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]  (61.5s)        
Statistics:
  BVH
    Interior nodes                                                3648372
    Leaf nodes                                                    3648373
    Primitives per leaf node                       4323664 /      3648373 (1.19x)
  Integrator
    Camera rays traced                                          268378112
    Path length                                                     1.316 avg [range 0 - 4]
    Zero-radiance paths                          146856926 /    353774467 (41.51%)
  Intersections
    Regular ray intersection tests                              680824749
    Shadow ray intersection tests                               217814691
    Ray-triangle intersection tests              435722899 /   3289961203 (13.24%)
  Memory
    BVH tree                                                       288.65 MiB
    Film pixels                                                     15.82 MiB
    Primitives                                                     296.88 MiB
    Texture MIP maps                                               257.00 MiB
    TransformCache                                                 260.00 kB
    Triangle meshes                                                385.41 MiB
  Scene
    AreaLights                                                          4
    Lights                                                              5
    Materials created                                                   1
    Shapes created                                                4323664
    Probes per TransformCache lookup                                1.000 avg [range 1 - 1]
    TransformCache hits                                  8 /           10 (80.00%)
    Triangles per triangle mesh                    4323664 /            4 (1080916.00x)
  SpatialLightDistribution
    Distributions created                                            4212
    Hash probes per lookup                                          1.002 avg [range 1 - 3]
    Lookups per distribution                     353774467 /         4212 (83992.04x)
  Texture
    EWA lookups                                                 162214764
    Trilinear lookups                                           145501962
  Profile
    Integrator::Render()                                            96.62% (   0:01:06.63)
      Camera::GenerateRay[Differential]()                            2.12% (   0:00:01.46)
      Film::AddSample()                                              3.27% (   0:00:02.25)
      Film::MergeTile()                                              0.00% (   0:00:00.00)
      Sampler::GetSample[12]D()                                      4.88% (   0:00:03.36)
      Sampler::StartPixelSample()                                    0.00% (   0:00:00.00)
      SamplerIntegrator::Li()                                       84.30% (   0:00:58.13)
        Accelerator::Intersect()                                    36.19% (   0:00:24.96)
          Triangle::Intersect()                                      8.14% (   0:00:05.61)
        BSDF::Sample_f()                                             3.15% (   0:00:02.16)
        Direct lighting                                             33.99% (   0:00:23.44)
          Accelerator::Intersect()                                   3.77% (   0:00:02.60)
            Triangle::Intersect()                                    0.74% (   0:00:00.51)
          Accelerator::IntersectP()                                 12.32% (   0:00:08.49)
            Triangle::IntersectP()                                   2.36% (   0:00:01.62)
          BSDF::PDF()                                                0.71% (   0:00:00.48)
          BSDF::Sample_f()                                           3.31% (   0:00:02.28)
          BSDF::f()                                                  0.59% (   0:00:00.40)
          Light::Pdf()                                               2.10% (   0:00:01.45)
            Triangle::Intersect()                                    0.86% (   0:00:00.59)
          Light::Sample_*()                                          2.93% (   0:00:02.02)
            MIPMap::Lookup() (trilinear)                             0.55% (   0:00:00.38)
          MIPMap::Lookup() (trilinear)                               0.26% (   0:00:00.18)
          Sampler::GetSample[12]D()                                  3.99% (   0:00:02.74)
        MIPMap::Lookup() (trilinear)                                 0.87% (   0:00:00.60)
        Material::ComputeScatteringFunctions()                       3.20% (   0:00:02.20)
          MIPMap::Lookup() (EWA)                                     2.21% (   0:00:01.52)
        Sampler::GetSample[12]D()                                    1.69% (   0:00:01.16)
        SpatialLightDistribution lookup                              0.77% (   0:00:00.52)
          SpatialLightDistribution creation                          0.03% (   0:00:00.02)
            Light::Sample_*()                                        0.02% (   0:00:00.01)
              MIPMap::Lookup() (trilinear)                           0.01% (   0:00:00.00)
    Scene parsing and creation                                       3.38% (   0:00:02.33)
      Acceleration structure creation                                1.56% (   0:00:01.07)
      MIPMap::Lookup() (trilinear)                                   0.00% (   0:00:00.00)
      Texture loading                                                0.77% (   0:00:00.52)
        MIP map generation                                           0.37% (   0:00:00.25)
  Profile (flattened)
    Accelerator::Intersect()                                        31.09% (   0:00:21.43)
    Sampler::GetSample[12]D()                                       10.55% (   0:00:07.27)
    Accelerator::IntersectP()                                        9.96% (   0:00:06.86)
    Triangle::Intersect()                                            9.74% (   0:00:06.71)
    BSDF::Sample_f()                                                 6.45% (   0:00:04.44)
    SamplerIntegrator::Li()                                          4.44% (   0:00:03.06)
    Direct lighting                                                  4.01% (   0:00:02.76)
    Film::AddSample()                                                3.27% (   0:00:02.25)
    Light::Sample_*()                                                2.39% (   0:00:01.65)
    Triangle::IntersectP()                                           2.36% (   0:00:01.62)
    MIPMap::Lookup() (EWA)                                           2.21% (   0:00:01.52)
    Camera::GenerateRay[Differential]()                              2.12% (   0:00:01.46)
    Integrator::Render()                                             2.04% (   0:00:01.40)
    MIPMap::Lookup() (trilinear)                                     1.70% (   0:00:01.17)
    Acceleration structure creation                                  1.56% (   0:00:01.07)
    Light::Pdf()                                                     1.25% (   0:00:00.85)
    Scene parsing and creation                                       1.06% (   0:00:00.72)
    Material::ComputeScatteringFunctions()                           0.99% (   0:00:00.68)
    SpatialLightDistribution lookup                                  0.74% (   0:00:00.50)
    BSDF::PDF()                                                      0.71% (   0:00:00.48)
    BSDF::f()                                                        0.59% (   0:00:00.40)
    Texture loading                                                  0.40% (   0:00:00.27)
    MIP map generation                                               0.37% (   0:00:00.25)
    SpatialLightDistribution creation                                0.01% (   0:00:00.00)
    Film::MergeTile()                                                0.00% (   0:00:00.00)
    Sampler::StartPixelSample()                                      0.00% (   0:00:00.00)


real	1m9,062s
user	28m15,524s
sys	0m1,292s

Because rs_pbrt renders currently to a PNG file (pbrt.png) and the C++ version of pbrt-v3 to an OpenEXR file, we have to convert the image first before we can compare both images:

$ /media/datadisk3/git/github/pbrt-v4/build/imgtool convert ganesha.exr --outfile ganesha_pbrt_v3.png
$ imf_diff ganesha_pbrt_v3.png /media/datadisk3/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_ganesha/pbrt.png
differing pixels:	  0.004% (21 of 518400)
average difference:	  1.674%
maximum difference:	  3.069%
Summary: A few pixels differ slightly.
== "ganesha_pbrt_v3.png" and "/media/datadisk3/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_ganesha/pbrt.png" are similar

There was a time where the Rust version was rendering both a PNG and an OpenEXR file, but currently this is disabled, I might bring that back one day (after I adjusted the code to the latest openexr crate).

Anyway, the ganesha scene renders slightly different for pbrt-v4 (so either the material or the color correction is differently implemented).

pbrt-v4 connecting to tev

$ time /media/datadisk3/git/github/pbrt-v4/build/pbrt --display-server localhost:14158 ganesha.pbrt 
pbrt version 4 (built Sep  5 2023 at 12:50:51)
Copyright (c)1998-2021 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
The source code to pbrt (but *not* the book contents) is covered by the Apache 2.0 License.
See the file LICENSE.txt for the conditions of the license.
Rendering: [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]  (80.9s)        

real	1m26,190s
user	37m28,375s
sys	0m1,449s

So the C++ version of pbrt-v4 is slower than the previous pbrt-v3 C++ version (but renders in a similar time to the Rust implementation), using the CPUs, but as soon as you enable the GPU things get fast (depends on your graphics card):

$ time /media/datadisk3/git/github/pbrt-v4/build/pbrt --display-server localhost:14158 --gpu ganesha.pbrt 
pbrt version 4 (built Sep  5 2023 at 12:50:51)
Copyright (c)1998-2021 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
The source code to pbrt (but *not* the book contents) is covered by the Apache 2.0 License.
See the file LICENSE.txt for the conditions of the license.
Rendering: [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]  (6.5s)       

real	0m9,541s
user	0m11,811s
sys	0m2,107s

To compare both images (rendered on the CPU vs. the GPU) you can select both images within tev and select a couple of buttons (E for Error, AE for Absolute Error, SE for Squared Error, RAE for Relative Absolute Error, and RSE for Relative Squared Error):

Compare OpenEXR files within tev

You can also compare both images via the imf_diff tool:

$ imf_diff -d -f ganesha_?pu.exr ganesha_diff.jpg
differing pixels:	 11.242% (58281 of 518400)
average difference:	  1.520%
maximum difference:	  9.705%
Summary: Many pixels differ slightly.
== "ganesha_cpu.exr" and "ganesha_gpu.exr" are different

Compare OpenEXR via imf_diff

Currently the connection to tev works for the Rust renderer rs_pbrt only for the integrators which use the SamplerIntegrator render loop. This will most likely be changed soon and before the tev branch gets merged into the master branch.

Finally, I was interested how to watch the GPUs rendering (on Linux) and found a command line tool, called nvtop (NVIDIA GPU top):

Using NVIDIA GPU top while rendering on the GPU

Back to top