Commit: 077116f6570a4f22e6b1d0e875d8c68f518a3837
Author: Lukas Stockner
Date:   Sat Aug 6 05:04:12 2016 +0200
Branches: soc-2016-cycles_denoising
https://developer.blender.org/rB077116f6570a4f22e6b1d0e875d8c68f518a3837

Cycles: Redesign the buffer accesses of the denoising kernel.

At first, the denoising kernel just directly accessed the RenderBuffers.
However, that introduced some addressing complexity since the filter window 
might cover multiple tiles, each with a separate buffer.
Apart from the addressing overhead, this also made it pretty much impossible to 
SIMDify the CPU code.

When feature prefiltering was added, it changed the buffer addressing.
First, it copied the various parts of different buffers into one continuous 
array. Then, it operated directly on that array.
With these changes, the only thing the regular buffer addressing was still 
needed for was the color image.

Now, this commit also copies the color image into the prefiltered buffer. 
Therefore, it's not really just a prefiltered buffer anymore, but actually 
contains all the data needed to denoise.
This allows to redesign and clean up the kernel-device-interface, which is also 
done in this commit.

Advantages are:
 - Lower addressing overhead - every pixel is only addressed once to copy the 
data to the denoising buffer, and once to store the final result - instead of 
hundreds of accesses per pixel when looping over the filter window.
 - Lower code complexity - one array with standard scanline addressing makes 
the code a lot cleaner.
 - For GPUs: More memory access coherence since the passes are stored in SoA 
layout instead of AoS (like the regular RenderBuffers are).
 - For CPUs: Possibility to use SIMD instructions in the future due to the SoA 
layout.
The disadvantage is slightly higher memory usage - 22 floats per pixel instead 
of 16.

This commit doesn't include the CUDA changes yet.

===================================================================

M       intern/cycles/device/device_cpu.cpp
M       intern/cycles/kernel/kernel_filter.h
M       intern/cycles/kernel/kernels/cpu/kernel_cpu.h
M       intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h
M       intern/cycles/kernel/kernels/cuda/kernel.cu
M       intern/cycles/util/util_types.h

===================================================================

diff --git a/intern/cycles/device/device_cpu.cpp 
b/intern/cycles/device/device_cpu.cpp
index adc0414..9ca1d03 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -208,7 +208,7 @@ public:
                }
        };
 
-       float* denoise_prefilter(int4 prefilter_rect, RenderTile &tile, 
KernelGlobals *kg, int sample, float** buffers, int* tile_x, int* tile_y, int 
*offsets, int *strides)
+       float* denoise_fill_buffer(KernelGlobals *kg, int sample, int4 rect, 
float** buffers, int* tile_x, int* tile_y, int *offsets, int *strides)
        {
                void(*filter_divide_shadow)(KernelGlobals*, int, float**, int, 
int, int*, int*, int*, int*, float*, float*, float*, float*, int4);
                void(*filter_get_feature)(KernelGlobals*, int, float**, int, 
int, int, int, int*, int*, int*, int*, float*, float*, int4);
@@ -267,129 +267,221 @@ public:
                        filter_combine_halves = 
kernel_cpu_filter_combine_halves;
                }
 
-               int w = (prefilter_rect.z - prefilter_rect.x), h = 
(prefilter_rect.w - prefilter_rect.y);
-               float *prefiltered = new float[16*w*h];
-               float *unfiltered = new float[2*w*h];
+               int w = align_up(rect.z - rect.x, 4), h = (rect.w - rect.y);
+               float *filter_buffer = new float[22*w*h];
 
 
 
-               /* Prefilter general features. */
-               int m_offsets[] = {0, 1, 2, 6, 7, 8, 12};
-               int variances[] = {3, 4, 5, 9, 10, 11, 13};
-               for(int i = 0; i < 7; i++) {
-                       for(int y = prefilter_rect.y; y < prefilter_rect.w; 
y++) {
-                               for(int x = prefilter_rect.x; x < 
prefilter_rect.z; x++) {
-                                       filter_get_feature(kg, sample, buffers, 
m_offsets[i], variances[i], x, y, tile_x, tile_y, offsets, strides, unfiltered, 
prefiltered + (2*i+1)*w*h, prefilter_rect);
+               /* ==== Step 1: Prefilter general features. ==== */
+               {
+                       float *unfiltered = filter_buffer + 16*w*h;
+                       /* Order in render buffers:
+                        *   Normal[X, Y, Z] NormalVar[X, Y, Z] Albedo[R, G, B] 
AlbedoVar[R, G, B ] Depth DepthVar
+                        *          0  1  2            3  4  5         6  7  8  
          9  10 11  12    13
+                        *
+                        * Order in denoise buffer:
+                        *   Normal[X, XVar, Y, YVar, Z, ZVar] Depth DepthVar 
Shadow ShadowVar Albedo[R, RVar, G, GVar, B, BVar] Color[R, RVar, G, GVar, B, 
BVar]
+                        *          0  1     2  3     4  5     6     7        8 
     9                10 11    12 13    14 15          16 17    18 19    20 21
+                        *
+                        * Order of processing: |NormalXYZ|Depth|AlbedoXYZ |
+                        *                      |         |     |          | */
+                       int mean_from[]      = { 0, 1, 2,   6,    7,  8, 12 };
+                       int variance_from[]  = { 3, 4, 5,   9,   10, 11, 13 };
+                       int offset_to[]      = { 0, 2, 4,  10,   12, 14,  6 };
+                       for(int i = 0; i < 7; i++) {
+                               for(int y = rect.y; y < rect.w; y++) {
+                                       for(int x = rect.x; x < rect.z; x++) {
+                                               filter_get_feature(kg, sample, 
buffers, mean_from[i], variance_from[i], x, y, tile_x, tile_y, offsets, 
strides, unfiltered, filter_buffer + (offset_to[i]+1)*w*h, rect);
+                                       }
                                }
-                       }
-                       for(int y = prefilter_rect.y; y < prefilter_rect.w; 
y++) {
-                               for(int x = prefilter_rect.x; x < 
prefilter_rect.z; x++) {
-                                       filter_non_local_means(x, y, 
unfiltered, unfiltered, prefiltered + (2*i+1)*w*h, prefiltered + 2*i*w*h, 
prefilter_rect, 2, 2, 1, 0.25f);
+                               for(int y = rect.y; y < rect.w; y++) {
+                                       for(int x = rect.x; x < rect.z; x++) {
+                                               filter_non_local_means(x, y, 
unfiltered, unfiltered, filter_buffer + (offset_to[i]+1)*w*h, filter_buffer + 
offset_to[i]*w*h, rect, 2, 2, 1, 0.25f);
+                                       }
                                }
-                       }
 #ifdef WITH_CYCLES_DEBUG_FILTER
 #define WRITE_DEBUG(name, var) 
debug_write_pfm(string_printf("debug_%dx%d_feature%d_%s.pfm", tile.x, tile.y, 
i, name).c_str(), var, w, h, 1, w)
-                       WRITE_DEBUG("unfiltered", unfiltered);
-                       WRITE_DEBUG("sampleV", prefiltered + (2*i+1)*w*h);
-                       WRITE_DEBUG("filtered", prefiltered + 2*i*w*h);
+                               WRITE_DEBUG("unfiltered", unfiltered);
+                               WRITE_DEBUG("sampleV", filter_buffer + 
(offset_to[i]+1)*w*h);
+                               WRITE_DEBUG("filtered", filter_buffer + 
offset_to[i]*w*h);
 #undef WRITE_DEBUG
 #endif
+                       }
                }
 
 
 
-
-
-
-
-
-
-
-
-               float *sampleV = prefiltered + 14*w*h, *sampleVV = new 
float[w*h], *bufferV = prefiltered + 15*w*h, *cleanV = new float[w*h];
-
-               /* Get the A/B unfiltered passes, the combined sample variance, 
the estimated variance of the sample variance and the buffer variance. */
-               for(int y = prefilter_rect.y; y < prefilter_rect.w; y++) {
-                       for(int x = prefilter_rect.x; x < prefilter_rect.z; 
x++) {
-                               filter_divide_shadow(kg, sample, buffers, x, y, 
tile_x, tile_y, offsets, strides, unfiltered, sampleV, sampleVV, bufferV, 
prefilter_rect);
+               /* ==== Step 2: Prefilter shadow feature. ==== */
+               {
+                       /* Reuse some passes of the filter_buffer for temporary 
storage. */
+                       float *sampleV = filter_buffer + 16*w*h, *sampleVV = 
filter_buffer + 17*w*h, *bufferV = filter_buffer + 18*w*h, *cleanV = 
filter_buffer + 19*w*h;
+                       float *unfiltered = filter_buffer + 20*w*h;
+
+                       /* Get the A/B unfiltered passes, the combined sample 
variance, the estimated variance of the sample variance and the buffer 
variance. */
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_divide_shadow(kg, sample, 
buffers, x, y, tile_x, tile_y, offsets, strides, unfiltered, sampleV, sampleVV, 
bufferV, rect);
+                               }
                        }
-               }
 #ifdef WITH_CYCLES_DEBUG_FILTER
 #define WRITE_DEBUG(name, var) 
debug_write_pfm(string_printf("debug_%dx%d_shadow_%s.pfm", tile.x, tile.y, 
name).c_str(), var, w, h, 1, w)
-               WRITE_DEBUG("unfilteredA", unfiltered);
-               WRITE_DEBUG("unfilteredB", unfiltered + w*h);
-               WRITE_DEBUG("bufferV", bufferV);
-               WRITE_DEBUG("sampleV", sampleV);
-               WRITE_DEBUG("sampleVV", sampleVV);
+                       WRITE_DEBUG("unfilteredA", unfiltered);
+                       WRITE_DEBUG("unfilteredB", unfiltered + w*h);
+                       WRITE_DEBUG("bufferV", bufferV);
+                       WRITE_DEBUG("sampleV", sampleV);
+                       WRITE_DEBUG("sampleVV", sampleVV);
 #endif
 
-
-
-               /* Smooth the (generally pretty noisy) buffer variance using 
the spatial information from the sample variance. */
-               for(int y = prefilter_rect.y; y < prefilter_rect.w; y++) {
-                       for(int x = prefilter_rect.x; x < prefilter_rect.z; 
x++) {
-                               //filter_prefilter_features(&kg, sample, x, y, 
filteredA, filteredB, prefilter_rect);
-                               filter_non_local_means(x, y, bufferV, sampleV, 
sampleVV, cleanV, prefilter_rect, 3, 1, 4, 1.0f);
+                       /* Smooth the (generally pretty noisy) buffer variance 
using the spatial information from the sample variance. */
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_non_local_means(x, y, bufferV, 
sampleV, sampleVV, cleanV, rect, 3, 1, 4, 1.0f);
+                               }
                        }
-               }
 #ifdef WITH_CYCLES_DEBUG_FILTER
                WRITE_DEBUG("cleanV", cleanV);
 #endif
 
-
-
-               /* Use the smoothed variance to filter the two shadow half 
images using each other for weight calculation. */
-               for(int y = prefilter_rect.y; y < prefilter_rect.w; y++) {
-                       for(int x = prefilter_rect.x; x < prefilter_rect.z; 
x++) {
-                               filter_non_local_means(x, y, unfiltered, 
unfiltered + w*h, cleanV, sampleV, prefilter_rect, 5, 3, 1, 0.25f);
-                               filter_non_local_means(x, y, unfiltered + w*h, 
unfiltered, cleanV, bufferV, prefilter_rect, 5, 3, 1, 0.25f);
+                       /* Use the smoothed variance to filter the two shadow 
half images using each other for weight calculation. */
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_non_local_means(x, y, 
unfiltered, unfiltered + w*h, cleanV, sampleV, rect, 5, 3, 1, 0.25f);
+                                       filter_non_local_means(x, y, unfiltered 
+ w*h, unfiltered, cleanV, bufferV, rect, 5, 3, 1, 0.25f);
+                               }
                        }
-               }
-               delete[] cleanV;
 #ifdef WITH_CYCLES_DEBUG_FILTER
-               WRITE_DEBUG("filteredA", sampleV);
-               WRITE_DEBUG("filteredB", bufferV);
+                       WRITE_DEBUG("filteredA", sampleV);
+                       WRITE_DEBUG("filteredB", bufferV);
 #endif
 
+                       /* Estimate the residual variance between the two 
filtered halves. */
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_combine_halves(x, y, NULL, 
sampleVV, sampleV, bufferV, rect);
+                               }
+                       }
+#ifdef WITH_CYCLES_DEBUG_FILTER
+                       WRITE_DEBUG("residualV", sampleVV);
+#endif
 
+                       /* Use the residual variance for a second filter pass. 
*/
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_non_local_means(x, y, sampleV, 
bufferV, sampleVV, unfiltered      , rect, 4, 2, 1, 0.25f);
+                                       filter_non_local_means(x, y, bufferV, 
sampleV, sampleVV, unfiltered + w*h, rect, 4, 2, 1, 0.25f);
+                               }
+                       }
+#ifdef WITH_CYCLES_DEBUG_FILTER
+                       WRITE_DEBUG("finalA", unfiltered);
+                       WRITE_DEBUG("finalB", unfiltered + w*h);
+#endif
 
-               /* Estimate the residual variance between the two filtered 
halves. */
-               for(int y = prefilter_rect.y; y < prefilter_rect.w; y++) {
-                       for(int x = prefilter_rect.x; x < prefilter_rect.z; 
x++) {
-                               filter_combine_halves(x, y, NULL, sampleVV, 
sampleV, bufferV, prefilter_rect);
+                       /* Combine the two double-filtered halves to a final 
shadow feature image and associated variance. */
+                       for(int y = rect.y; y < rect.w; y++) {
+                               for(int x = rect.x; x < rect.z; x++) {
+                                       filter_combine_halves(x, y, 
filter_buffer + 8*w*h, filter_buffer + 9*w*h, unfiltered, unfiltered + w*h, 
rect);
+                               }
                        }
-               }
 #ifdef WITH_CYCLES_DEBUG_FILTER
-               WRITE_DEBUG("residualV", sampleVV);
+                       WRITE_DEBUG("final", filter_buffer + 8*w*h);
+                       WRITE_DEBUG("finalV", filter_buffer + 9*w*h);
+#undef WRITE_DEBUG
 #endif
+               }
+
+
 
-               /* Use the residual variance for a second filter pass. */
-               for(int y = prefilter_rect.y; y < prefilter_rect.w; y++) {
-                       for(int x = prefilter_rect.x; x < prefilter_rect.z; 
x++) {
-                               filter_non_local_means(x, y, sampleV, bufferV, 
sampleVV, unfiltered      , prefilter_rect, 4, 2, 1, 0.25f);
-                               filter_non_local_means(x, y, bufferV, sampleV, 
sampleVV, unfiltered + w*h, prefilter_rect, 4, 2, 1, 0.25f);
+               /* ==== Step 3: Copy combined color pass. ==== */
+               {
+                       int mean_from[]      = {20, 21, 22};
+                       int variance_from[]  = {23, 24, 25};
+                       int offset_to[]      = {16, 18, 20};
+                       for(int i = 0; i < 3; i++) {
+                               for(int y = rect.y; y < rect.w; y++) {
+                                       for(int x = rect.x; x < rect.z; x++) {
+                                               filter_get_feature(kg, sample, 
buffers, mean_from

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to