[Bf-blender-cvs] [fb0596f75b] soc-2016-cycles_denoising: Cycles Denoising: Implement device-independent denoising algorithm to deduplicate device code

Lukas Stockner noreply at git.blender.org
Thu Feb 9 14:39:36 CET 2017


Commit: fb0596f75b25bb9d34b870a11b99856a2d2054b2
Author: Lukas Stockner
Date:   Wed Feb 8 16:57:53 2017 +0100
Branches: soc-2016-cycles_denoising
https://developer.blender.org/rBfb0596f75b25bb9d34b870a11b99856a2d2054b2

Cycles Denoising: Implement device-independent denoising algorithm to deduplicate device code

Instead of implementing the full algorithm inside the device, the new code just calls the device in order to run specific kernels while handling all the high-level logic outside of the individual devices.

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

M	intern/cycles/device/CMakeLists.txt
M	intern/cycles/device/device.h
M	intern/cycles/device/device_cpu.cpp
A	intern/cycles/device/device_denoising.cpp
A	intern/cycles/device/device_denoising.h
M	intern/cycles/device/device_memory.h

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

diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index 2d20e7138f..2478e3e226 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SRC
 	device.cpp
 	device_cpu.cpp
 	device_cuda.cpp
+	device_denoising.cpp
 	device_multi.cpp
 	device_opencl.cpp
 	device_task.cpp
@@ -54,6 +55,7 @@ endif()
 
 set(SRC_HEADERS
 	device.h
+	device_denoising.h
 	device_memory.h
 	device_intern.h
 	device_network.h
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index ccee25ae34..a06f171954 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -240,6 +240,14 @@ public:
 		int y, int w, int h, int elem) = 0;
 	virtual void mem_zero(device_memory& mem) = 0;
 	virtual void mem_free(device_memory& mem) = 0;
+	virtual device_ptr mem_get_offset_ptr(device_memory& mem, int offset)
+	{
+		/* Only required for devices that implement denoising. */
+		assert(false);
+		(void) mem;
+		(void) offset;
+		return (device_ptr) 0;
+	}
 
 	/* constant memory */
 	virtual void const_copy_to(const char *name, void *host, size_t size) = 0;
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index a1c77894fe..ebd4acb1e5 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -196,7 +196,7 @@ public:
 
 	void mem_alloc(device_memory& mem, MemoryType /*type*/)
 	{
-		mem.device_pointer = mem.data_pointer;
+		mem.device_pointer = mem.data_pointer? mem.data_pointer : ((device_ptr) new char[mem.memory_size()]);
 		mem.device_size = mem.memory_size();
 		stats.mem_alloc(mem.device_size);
 	}
diff --git a/intern/cycles/device/device_denoising.cpp b/intern/cycles/device/device_denoising.cpp
new file mode 100644
index 0000000000..f5b295f222
--- /dev/null
+++ b/intern/cycles/device/device_denoising.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2011-2017 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device_denoising.h"
+
+#include "filter_defines.h"
+
+CCL_NAMESPACE_BEGIN
+
+void DenoisingTask::init_from_kerneldata(KernelData *data)
+{
+	half_window = data->integrator.half_window;
+	pca_threshold = data->integrator.filter_strength;
+	nlm_k_2 = data->integrator.weighting_adjust;
+	use_cross_denoising = data->film.denoise_cross;
+	use_gradients = data->integrator.use_gradients;
+
+	render_buffer.pass_stride = data->film.pass_stride;
+	render_buffer.denoising_offset = data->film.pass_denoising;
+	render_buffer.no_denoising_offset = data->film.pass_no_denoising;
+	render_buffer.offset = neighbors.offsets[4];
+	render_buffer.stride = neighbors.strides[4];
+	render_buffer.ptr    = neighbors.buffers[4];
+
+	/* Expand filter_area by half_window pixels and clamp the result to the extent of the neighboring tiles. */
+	rect = make_int4(max(neighbors.tile_x[0], filter_area.x - half_window),
+	                 max(neighbors.tile_y[0], filter_area.y - half_window),
+	                 min(neighbors.tile_x[3], filter_area.x + filter_area.z + half_window),
+	                 min(neighbors.tile_y[3], filter_area.y + filter_area.w + half_window));
+}
+
+void DenoisingTask::NeighborBuffers::init_from_single_tile(const RenderTile &tile)
+{
+	tile_x[0] = tile.x;
+	tile_x[1] = tile.x;
+	tile_x[2] = tile.x+tile.w;
+	tile_x[3] = tile.x+tile.w;
+	tile_y[0] = tile.y;
+	tile_y[1] = tile.y;
+	tile_y[2] = tile.y+tile.h;
+	tile_y[3] = tile.y+tile.h;
+	std::fill(buffers, buffers+9, (device_ptr) 0);
+	std::fill(offsets, offsets+9, 0);
+	std::fill(strides, strides+9, 0);
+	buffers[4] = tile.buffer;
+	offsets[4] = tile.offset;
+	strides[4] = tile.stride;
+}
+
+void DenoisingTask::NeighborBuffers::init_from_rendertiles(RenderTile *rtiles)
+{
+	for(int i = 0; i < 9; i++) {
+		buffers[i] = rtiles[i].buffer;
+		offsets[i] = rtiles[i].offset;
+		strides[i] = rtiles[i].stride;
+	}
+	tile_x[0] = rtiles[3].x;
+	tile_x[1] = rtiles[4].x;
+	tile_x[2] = rtiles[5].x;
+	tile_x[3] = rtiles[5].x + rtiles[5].w;
+	tile_y[0] = rtiles[1].y;
+	tile_y[1] = rtiles[4].y;
+	tile_y[2] = rtiles[7].y;
+	tile_y[3] = rtiles[7].y + rtiles[7].h;
+}
+
+bool DenoisingTask::run_denoising()
+{
+	/* Allocate denoising buffer. */
+	buffer.passes = use_cross_denoising? 20 : 14;
+	buffer.w = align_up(rect.z - rect.x, 4);
+	buffer.h = rect.w - rect.y;
+	buffer.pass_stride = buffer.w * buffer.h;
+	buffer.mem.resize(buffer.pass_stride * buffer.passes);
+	device->mem_alloc(buffer.mem, MEM_READ_WRITE);
+
+	device_ptr null_ptr = (device_ptr) 0;
+
+	/* Prefilter shadow feature. */
+	{
+		device_ptr unfiltered_a, unfiltered_b, sample_var, sample_var_var, buffer_var, filtered_var;
+		unfiltered_a              = device->mem_get_offset_ptr(buffer.mem, 0);
+		unfiltered_b              = device->mem_get_offset_ptr(buffer.mem, 1*buffer.pass_stride);
+		sample_var                = device->mem_get_offset_ptr(buffer.mem, 2*buffer.pass_stride);
+		sample_var_var            = device->mem_get_offset_ptr(buffer.mem, 3*buffer.pass_stride);
+		buffer_var                = device->mem_get_offset_ptr(buffer.mem, 5*buffer.pass_stride);
+		filtered_var              = device->mem_get_offset_ptr(buffer.mem, 6*buffer.pass_stride);
+		nlm_state.temporary_1_ptr = device->mem_get_offset_ptr(buffer.mem, 7*buffer.pass_stride);
+		nlm_state.temporary_2_ptr = device->mem_get_offset_ptr(buffer.mem, 8*buffer.pass_stride);
+		nlm_state.temporary_3_ptr = device->mem_get_offset_ptr(buffer.mem, 9*buffer.pass_stride);
+
+		/* Get the A/B unfiltered passes, the combined sample variance, the estimated variance of the sample variance and the buffer variance. */
+		functions.divide_shadow(unfiltered_a, unfiltered_b, sample_var, sample_var_var, buffer_var);
+
+		/* Smooth the (generally pretty noisy) buffer variance using the spatial information from the sample variance. */
+		nlm_state.set_parameters(6, 3, 4.0f, 1.0f);
+		functions.non_local_means(buffer_var, sample_var, sample_var_var, filtered_var);
+
+		/* Reuse memory, the previous data isn't needed anymore. */
+		device_ptr filtered_a = buffer_var,
+		           filtered_b = sample_var;
+		/* Use the smoothed variance to filter the two shadow half images using each other for weight calculation. */
+		nlm_state.set_parameters(5, 3, 1.0f, 0.25f);
+		functions.non_local_means(unfiltered_a, unfiltered_b, filtered_var, filtered_a);
+		functions.non_local_means(unfiltered_b, unfiltered_a, filtered_var, filtered_b);
+
+		device_ptr residual_var = sample_var_var;
+		/* Estimate the residual variance between the two filtered halves. */
+		functions.combine_halves(filtered_a, filtered_b, null_ptr, residual_var, 2, rect);
+
+		device_ptr final_a = unfiltered_a,
+		           final_b = unfiltered_b;
+		/* Use the residual variance for a second filter pass. */
+		nlm_state.set_parameters(4, 2, 1.0f, 0.5f);
+		functions.non_local_means(filtered_a, filtered_b, residual_var, final_a);
+		functions.non_local_means(filtered_b, filtered_a, residual_var, final_b);
+
+		/* Combine the two double-filtered halves to a final shadow feature. */
+		device_ptr shadow_pass = device->mem_get_offset_ptr(buffer.mem, 4*buffer.pass_stride);
+		functions.combine_halves(final_a, final_b, shadow_pass, null_ptr, 0, rect);
+	}
+
+	/* Prefilter general features. */
+	{
+		device_ptr unfiltered, variance, feature_pass;
+		unfiltered                = device->mem_get_offset_ptr(buffer.mem,  8*buffer.pass_stride);
+		variance                  = device->mem_get_offset_ptr(buffer.mem,  9*buffer.pass_stride);
+		nlm_state.temporary_1_ptr = device->mem_get_offset_ptr(buffer.mem, 10*buffer.pass_stride);
+		nlm_state.temporary_2_ptr = device->mem_get_offset_ptr(buffer.mem, 11*buffer.pass_stride);
+		nlm_state.temporary_3_ptr = device->mem_get_offset_ptr(buffer.mem, 12*buffer.pass_stride);
+		int mean_from[]     = { 0, 1, 2, 6,  7,  8, 12 };
+		int variance_from[] = { 3, 4, 5, 9, 10, 11, 13 };
+		int pass_to[]       = { 1, 2, 3, 0,  5,  6,  7 };
+		for(int pass = 0; pass < 7; pass++) {
+			feature_pass = device->mem_get_offset_ptr(buffer.mem, pass_to[pass]*buffer.pass_stride);
+			/* Get the unfiltered pass and its variance from the RenderBuffers. */
+			functions.get_feature(mean_from[pass], variance_from[pass], unfiltered, variance);
+			/* Smooth the pass and store the result in the denoising buffers. */
+			nlm_state.set_parameters(2, 2, 1.0f, 0.25f);
+			functions.non_local_means(unfiltered, unfiltered, variance, feature_pass);
+		}
+	}
+
+	/* Copy color passes. */
+	{
+		device_ptr color_pass, color_var_pass;
+		int mean_from[]     = {20, 21, 22, 26, 27, 28};
+		int variance_from[] = {23, 24, 25, 29, 30, 31};
+		int mean_to[]       = { 8,  9, 10, 14, 15, 16};
+		int variance_to[]   = {11, 12, 13, 17, 18, 19};
+		int num_color_passes = use_cross_denoising? 6 : 3;
+		for(int pass = 0; pass < num_color_passes; pass++) {
+			color_pass     = device->mem_get_offset_ptr(buffer.mem,     mean_to[pass]*buffer.pass_stride);
+			color_var_pass = device->mem_get_offset_ptr(buffer.mem, variance_to[pass]*buffer.pass_stride);
+			functions.get_feature(mean_from[pass], variance_from[pass], color_pass, color_var_pass);
+		}
+	}
+
+	storage.w = filter_area.z;
+	storage.h = filter_area.w;
+	storage.transform.resize(storage.w, storage.h, TRANSFORM_SIZE);
+	storage.rank.resize(storage.w, storage.h);
+	device->mem_alloc(storage.transform, MEM_READ_WRITE);
+	device->mem_alloc(storage.rank, MEM_READ_WRITE);
+
+	functions.construct_transform();
+
+	device_only_memory<float> temporary_1(buffer.w, buffer.h);
+	device_only_memory<float> temporary_2(buffer.w, buffer.h);
+	device->mem_alloc(temporary_1, MEM_READ_WRITE);
+	device->mem_alloc(temporary_2, MEM_READ_WRITE);
+	reconst

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list