From 8393ccd07634b3152b18d4d527b1460dab9dbe06 Mon Sep 17 00:00:00 2001 From: Patrick Mours Date: Tue, 4 Jan 2022 21:39:54 +0100 Subject: Cycles: Add OptiX temporal denoising support Enables the `bpy.ops.cycles.denoise_animation()` operator again and modifies it to support temporal denoising with OptiX. This requires renders that were done with both the "Vector" and "Denoising Data" passes. Differential Revision: https://developer.blender.org/D11442 --- intern/cycles/device/denoise.cpp | 2 + intern/cycles/device/denoise.h | 6 ++- intern/cycles/device/optix/device_impl.cpp | 81 +++++++++++++++++++++++++++--- intern/cycles/device/optix/device_impl.h | 1 + intern/cycles/device/queue.h | 5 +- 5 files changed, 86 insertions(+), 9 deletions(-) (limited to 'intern/cycles/device') diff --git a/intern/cycles/device/denoise.cpp b/intern/cycles/device/denoise.cpp index c291a7a0adb..8ae2bb213e4 100644 --- a/intern/cycles/device/denoise.cpp +++ b/intern/cycles/device/denoise.cpp @@ -76,6 +76,8 @@ NODE_DEFINE(DenoiseParams) SOCKET_BOOLEAN(use_pass_albedo, "Use Pass Albedo", true); SOCKET_BOOLEAN(use_pass_normal, "Use Pass Normal", false); + SOCKET_BOOLEAN(temporally_stable, "Temporally Stable", false); + SOCKET_ENUM(prefilter, "Prefilter", *prefilter_enum, DENOISER_PREFILTER_FAST); return type; diff --git a/intern/cycles/device/denoise.h b/intern/cycles/device/denoise.h index 3f30506ae06..07868527fc5 100644 --- a/intern/cycles/device/denoise.h +++ b/intern/cycles/device/denoise.h @@ -72,6 +72,9 @@ class DenoiseParams : public Node { bool use_pass_albedo = true; bool use_pass_normal = true; + /* Configure the denoiser to use motion vectors, previous image and a temporally stable model. */ + bool temporally_stable = false; + DenoiserPrefilter prefilter = DENOISER_PREFILTER_FAST; static const NodeEnum *get_type_enum(); @@ -83,7 +86,8 @@ class DenoiseParams : public Node { { return !(use == other.use && type == other.type && start_sample == other.start_sample && use_pass_albedo == other.use_pass_albedo && - use_pass_normal == other.use_pass_normal && prefilter == other.prefilter); + use_pass_normal == other.use_pass_normal && + temporally_stable == other.temporally_stable && prefilter == other.prefilter); } }; diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index 38cc3330ebd..009661b2dec 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -566,6 +566,19 @@ class OptiXDevice::DenoiseContext { } } + if (denoise_params.temporally_stable) { + prev_output.device_pointer = render_buffers->buffer.device_pointer; + + prev_output.offset = buffer_params.get_pass_offset(PASS_DENOISING_PREVIOUS); + + prev_output.stride = buffer_params.stride; + prev_output.pass_stride = buffer_params.pass_stride; + + num_input_passes += 1; + use_pass_flow = true; + pass_motion = buffer_params.get_pass_offset(PASS_MOTION); + } + use_guiding_passes = (num_input_passes - 1) > 0; if (use_guiding_passes) { @@ -574,6 +587,7 @@ class OptiXDevice::DenoiseContext { guiding_params.pass_albedo = pass_denoising_albedo; guiding_params.pass_normal = pass_denoising_normal; + guiding_params.pass_flow = pass_motion; guiding_params.stride = buffer_params.stride; guiding_params.pass_stride = buffer_params.pass_stride; @@ -588,6 +602,10 @@ class OptiXDevice::DenoiseContext { guiding_params.pass_normal = guiding_params.pass_stride; guiding_params.pass_stride += 3; } + if (use_pass_flow) { + guiding_params.pass_flow = guiding_params.pass_stride; + guiding_params.pass_stride += 2; + } guiding_params.stride = buffer_params.width; @@ -605,6 +623,16 @@ class OptiXDevice::DenoiseContext { RenderBuffers *render_buffers = nullptr; const BufferParams &buffer_params; + /* Previous output. */ + struct { + device_ptr device_pointer = 0; + + int offset = PASS_UNUSED; + + int stride = -1; + int pass_stride = -1; + } prev_output; + /* Device-side storage of the guiding passes. */ device_only_memory guiding_buffer; @@ -614,6 +642,7 @@ class OptiXDevice::DenoiseContext { /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ int pass_albedo = PASS_UNUSED; int pass_normal = PASS_UNUSED; + int pass_flow = PASS_UNUSED; int stride = -1; int pass_stride = -1; @@ -624,6 +653,7 @@ class OptiXDevice::DenoiseContext { bool use_guiding_passes = false; bool use_pass_albedo = false; bool use_pass_normal = false; + bool use_pass_flow = false; int num_samples = 0; @@ -632,6 +662,7 @@ class OptiXDevice::DenoiseContext { /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ int pass_denoising_albedo = PASS_UNUSED; int pass_denoising_normal = PASS_UNUSED; + int pass_motion = PASS_UNUSED; /* For passes which don't need albedo channel for denoising we replace the actual albedo with * the (0.5, 0.5, 0.5). This flag indicates that the real albedo pass has been replaced with @@ -702,6 +733,7 @@ bool OptiXDevice::denoise_filter_guiding_preprocess(DenoiseContext &context) &context.guiding_params.pass_stride, &context.guiding_params.pass_albedo, &context.guiding_params.pass_normal, + &context.guiding_params.pass_flow, &context.render_buffers->buffer.device_pointer, &buffer_params.offset, &buffer_params.stride, @@ -709,6 +741,7 @@ bool OptiXDevice::denoise_filter_guiding_preprocess(DenoiseContext &context) &context.pass_sample_count, &context.pass_denoising_albedo, &context.pass_denoising_normal, + &context.pass_motion, &buffer_params.full_x, &buffer_params.full_y, &buffer_params.width, @@ -881,7 +914,8 @@ bool OptiXDevice::denoise_create_if_needed(DenoiseContext &context) { const bool recreate_denoiser = (denoiser_.optix_denoiser == nullptr) || (denoiser_.use_pass_albedo != context.use_pass_albedo) || - (denoiser_.use_pass_normal != context.use_pass_normal); + (denoiser_.use_pass_normal != context.use_pass_normal) || + (denoiser_.use_pass_flow != context.use_pass_flow); if (!recreate_denoiser) { return true; } @@ -895,8 +929,14 @@ bool OptiXDevice::denoise_create_if_needed(DenoiseContext &context) OptixDenoiserOptions denoiser_options = {}; denoiser_options.guideAlbedo = context.use_pass_albedo; denoiser_options.guideNormal = context.use_pass_normal; + + OptixDenoiserModelKind model = OPTIX_DENOISER_MODEL_KIND_HDR; + if (context.use_pass_flow) { + model = OPTIX_DENOISER_MODEL_KIND_TEMPORAL; + } + const OptixResult result = optixDenoiserCreate( - this->context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser_.optix_denoiser); + this->context, model, &denoiser_options, &denoiser_.optix_denoiser); if (result != OPTIX_SUCCESS) { set_error("Failed to create OptiX denoiser"); @@ -906,6 +946,7 @@ bool OptiXDevice::denoise_create_if_needed(DenoiseContext &context) /* OptiX denoiser handle was created with the requested number of input passes. */ denoiser_.use_pass_albedo = context.use_pass_albedo; denoiser_.use_pass_normal = context.use_pass_normal; + denoiser_.use_pass_flow = context.use_pass_flow; /* OptiX denoiser has been created, but it needs configuration. */ denoiser_.is_configured = false; @@ -965,8 +1006,10 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) OptixImage2D color_layer = {0}; OptixImage2D albedo_layer = {0}; OptixImage2D normal_layer = {0}; + OptixImage2D flow_layer = {0}; OptixImage2D output_layer = {0}; + OptixImage2D prev_output_layer = {0}; /* Color pass. */ { @@ -982,6 +1025,19 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) color_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; } + /* Previous output. */ + if (context.prev_output.offset != PASS_UNUSED) { + const int64_t pass_stride_in_bytes = context.prev_output.pass_stride * sizeof(float); + + prev_output_layer.data = context.prev_output.device_pointer + + context.prev_output.offset * sizeof(float); + prev_output_layer.width = width; + prev_output_layer.height = height; + prev_output_layer.rowStrideInBytes = pass_stride_in_bytes * context.prev_output.stride; + prev_output_layer.pixelStrideInBytes = pass_stride_in_bytes; + prev_output_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + /* Optional albedo and color passes. */ if (context.num_input_passes > 1) { const device_ptr d_guiding_buffer = context.guiding_params.device_pointer; @@ -1005,21 +1061,32 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) normal_layer.pixelStrideInBytes = pixel_stride_in_bytes; normal_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; } + + if (context.use_pass_flow) { + flow_layer.data = d_guiding_buffer + context.guiding_params.pass_flow * sizeof(float); + flow_layer.width = width; + flow_layer.height = height; + flow_layer.rowStrideInBytes = row_stride_in_bytes; + flow_layer.pixelStrideInBytes = pixel_stride_in_bytes; + flow_layer.format = OPTIX_PIXEL_FORMAT_FLOAT2; + } } /* Denoise in-place of the noisy input in the render buffers. */ output_layer = color_layer; - /* Finally run denoising. */ - OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ + OptixDenoiserGuideLayer guide_layers = {}; + guide_layers.albedo = albedo_layer; + guide_layers.normal = normal_layer; + guide_layers.flow = flow_layer; OptixDenoiserLayer image_layers = {}; image_layers.input = color_layer; + image_layers.previousOutput = prev_output_layer; image_layers.output = output_layer; - OptixDenoiserGuideLayer guide_layers = {}; - guide_layers.albedo = albedo_layer; - guide_layers.normal = normal_layer; + /* Finally run denoising. */ + OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ optix_assert(optixUtilDenoiserInvokeTiled(denoiser_.optix_denoiser, denoiser_.queue.stream(), diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h index 25073c60e69..a1865527c2d 100644 --- a/intern/cycles/device/optix/device_impl.h +++ b/intern/cycles/device/optix/device_impl.h @@ -104,6 +104,7 @@ class OptiXDevice : public CUDADevice { bool use_pass_albedo = false; bool use_pass_normal = false; + bool use_pass_flow = false; }; Denoiser denoiser_; diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 4e9f41f7875..926b7cba78a 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -19,6 +19,7 @@ #include "device/kernel.h" #include "device/graphics_interop.h" +#include "util/debug.h" #include "util/log.h" #include "util/map.h" #include "util/string.h" @@ -42,7 +43,7 @@ struct DeviceKernelArguments { KERNEL_FILM_CONVERT, }; - static const int MAX_ARGS = 16; + static const int MAX_ARGS = 18; Type types[MAX_ARGS]; void *values[MAX_ARGS]; size_t sizes[MAX_ARGS]; @@ -85,6 +86,8 @@ struct DeviceKernelArguments { } void add(const Type type, const void *value, size_t size) { + assert(count < MAX_ARGS); + types[count] = type; values[count] = (void *)value; sizes[count] = size; -- cgit v1.2.3