[Soc-2018-dev] Weekly Report #1 - Further Development for Cycles' Volume Rendering

Geraldine Chua chua.gsk at gmail.com
Tue May 22 16:17:31 CEST 2018


Hi Brecht,

The other part is how you map from the big grid to the tiles. The simplest
> solution is to use a two level grid, and that's what I suggest to do. To
> get an idea, this is how such a data structure and associated lookup
> function would work:
> https://developer.blender.org/P692
>

Thanks for the advice to use a double pointer for the grid! The only
solution I could think of to keep track of active tile coordinates was to
use map, which yes, is painfully inefficient.

RNA is not the right place to convert a dense grid to a sparse grid, it's
> going to be more complicated than it needs to be.
>

You're right, after some more reading, it seems like the best place to
insert the conversion should be somewhere in *render/image.cpp*. The
declaration of the grid should be something like:

//
*in ImageManager::device_load_image()*
else if(/* type of image is volume */) {
    device_vector<VolumeTile*> *tex_img = new
device_vector<VolumeTile*>(...);
    if(!file_load_image<TypeDesc::FLOAT, VolumeTile>(img, type,
texture_limit, *tex_img)) { /* ... */ }
    // ...
}

Since *ImageManager* only stores images as *device_memory*,
*device_vector<VolumeTile*>* can act as the VolumeGrid class from your
example while also being compatible with ImageManager. This also minimizes
the amount of dedicated code needed. I will then write some helper
functions for it e.g. init, iteration, lookup. Memory handling in theory
should already be handled by the existing class in so I do not think I will
need to make any modifications to *device/device_memory.h*.

For loading in particular, *ImageManager::file_load_image* in
*render/image.cpp* will need to be significantly modified to load the
volume into the device vector, and the conversion function inserted here
somwhere after the image is loaded. I have begun to make edits and I have
some more questions about this function (is this the right place to ask?)

1. What are components? Do volumes ever have >4 components? (since there is
more processing if components > 4)
2. In the function, after the image is loaded, there seems to be a lot of
pixel value changing, e.g.
            for(size_t i = num_pixels-1, pixel = 0; pixel < num_pixels;
pixel++, i--) {
                pixels[i*4+2] = (pixels[i*4+2]*pixels[i*4+3])/255;
                pixels[i*4+1] = (pixels[i*4+1]*pixels[i*4+3])/255;
                pixels[i*4+0] = (pixels[i*4+0]*pixels[i*4+3])/255;
                pixels[i*4+3] = alpha_one;
            }
but the kind of changing is conditional on the components? What is the
purpose of this part of the code? (in the latest master commit, this code
is in lines 576-651 of *render/image.cpp*)

kernel/kernels/cpu/kernel_cpu_image.h
> kernel/kernels/cuda/kernel_cuda_image.h
> kernel/kernels/opencl/kernel_opencl_image.h
>
> Lukas or I can help you figure out a design for the data structures, since
> the whole GPU and device memory aspect is rather complicated. We can start
> with a CPU implementation first but it should be along the right direction.
>

I will also have a look at these after image loading is functional. Thanks
for the tip! Unfortunately, I am not familiar with GPU programming.
Generally, what differences will need to be handled? I had heard that GPUs
are not very good at handling pointers and double precision arithmetic. So
do you think using *device_vector<VolumeTile*>* rather than a dedicated
class is alright?

I have some code written but generally I don't like to make commits unless
there is a whole feature fully implemented, I am too obsessed with clean
git histories :P . Would everyone rather I make commits of incomplete code
to show progress, or would it be alright for me to only commit once a
feature is mostly complete and functional?

Best regards,
Geraldine

On Mon, May 21, 2018 at 8:23 AM, Brecht Van Lommel <
brechtvanlommel at gmail.com> wrote:

> On Sun, May 20, 2018 at 7:11 PM, Geraldine Chua <chua.gsk at gmail.com>
> wrote:
>
>> # Texture saving and loading
>>
>> For the most part, it seems like hashtables are the most efficient way to
>> store sparse data sets. In order to optimize volume texture storage, I
>> traced the top-most relevant code to `ImageManager::file_load_image` in
>> `render/image.cpp`. It appears that smoke data specifically is retrieved
>> through the custom functions in `~/blender/makesrna/intern/rna_smoke.c`
>> rather than using OIIO. Thus, I can implement here a simple conversion of
>> the volume grid into a map of <coordinates, voxel value> pairs. All parent
>> functions will also need to be modified to be compatible with a map object
>> instead of a vector/array.
>>
>
> <coordinates, voxel value> pairs are not an efficient way to store and
> sample sparse volume data. If you have just a float (or half float) density
> grid the coordinates can take up 3x more space than the voxel values.
> Further hash tables will cause cache misses which is not good for rendering
> performance.
>
> The intention for the GSoC project was to use sparse 3D tiles, which are
> also used in OpenVDB and most other volume rendering or simulation
> software. What you do is split up the big 3D grid into smaller tiles of
> 8x8x8 voxels. If a tile contains no data its voxel values are not stored.
> The code in mesh_volume.c already detects empty 8x8x8 tiles for creating
> bounds, so some of that code can perhaps be reused.
>
> The other part is how you map from the big grid to the tiles. The simplest
> solution is to use a two level grid, and that's what I suggest to do. To
> get an idea, this is how such a data structure and associated lookup
> function would work:
> https://developer.blender.org/P692
>
> In practice it will be a bit different to make it work well for GPUs.
>
> # To-do next week
>>
>> * Modify the `rna_SmokeModifier_*_grid_get` functions to optionally
>> recieve a thresholding value and return an array of coordinates + values of
>> all active voxels.
>> * Modify callers of `rna_SmokeModifier_*_grid_get` to properly interpret
>> and manipulate the new array type.
>>
>
> RNA is not the right place to convert a dense grid to a sparse grid, it's
> going to be more complicated than it needs to be. It will be good if
> Blender itself supports sparse grids in the future, but when it does there
> will be no need for thresholding in RNA, rather it would be exposing
> existing tiled data.
>
> Most of the work for Cycles is supporting tiled 3D image storage and
> sampling. That means changing mostly the following files :
> render/image.cpp
> render/image.h
> device/device_memory.h
> kernel/kernels/cpu/kernel_cpu_image.h
> kernel/kernels/cuda/kernel_cuda_image.h
> kernel/kernels/opencl/kernel_opencl_image.h
>
> Lukas or I can help you figure out a design for the data structures, since
> the whole GPU and device memory aspect is rather complicated. We can start
> with a CPU implementation first but it should be along the right direction.
>
>
>> * Create a git branch for code changes.
>> * Make comparison tests on memory performance.
>> * If have time, move on to importing OpenVDB files.
>>
>
> You can do either OpenVDB import first, or do tiled 3D images first, but
> both will take at least some weeks to finish fully.
>
>
> --
> Soc-2018-dev mailing list
> Soc-2018-dev at blender.org
> https://lists.blender.org/mailman/listinfo/soc-2018-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.blender.org/pipermail/soc-2018-dev/attachments/20180522/cdfd515c/attachment-0001.html>


More information about the Soc-2018-dev mailing list