Blender V5.0
denoiser_optix.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#ifdef WITH_OPTIX
6
9
11# include "device/optix/queue.h"
12
13# include <optix_denoiser_tiling.h>
14
16
17OptiXDenoiser::OptiXDenoiser(Device *denoiser_device, const DenoiseParams &params)
18 : DenoiserGPU(denoiser_device, params), state_(denoiser_device, "__denoiser_state", true)
19{
20}
21
22OptiXDenoiser::~OptiXDenoiser()
23{
24 /* It is important that the OptixDenoiser handle is destroyed before the OptixDeviceContext
25 * handle, which is guaranteed since the local denoising device owning the OptiX device context
26 * is deleted as part of the Denoiser class destructor call after this. */
27 if (optix_denoiser_ != nullptr) {
28 optixDenoiserDestroy(optix_denoiser_);
29 }
30}
31
32uint OptiXDenoiser::get_device_type_mask() const
33{
34 return DEVICE_MASK_OPTIX;
35}
36
37bool OptiXDenoiser::is_device_supported(const DeviceInfo &device)
38{
39 if (device.type == DEVICE_OPTIX) {
40 return device.denoisers & DENOISER_OPTIX;
41 }
42 return false;
43}
44
45bool OptiXDenoiser::denoise_buffer(const DenoiseTask &task)
46{
47 OptiXDevice *const optix_device = static_cast<OptiXDevice *>(denoiser_device_);
48
49 const CUDAContextScope scope(optix_device);
50
51 return DenoiserGPU::denoise_buffer(task);
52}
53
54bool OptiXDenoiser::denoise_create_if_needed(DenoiseContext &context)
55{
56 const bool recreate_denoiser = (optix_denoiser_ == nullptr) ||
57 (use_pass_albedo_ != context.use_pass_albedo) ||
58 (use_pass_normal_ != context.use_pass_normal) ||
59 (use_pass_motion_ != context.use_pass_motion);
60 if (!recreate_denoiser) {
61 return true;
62 }
63
64 /* Destroy existing handle before creating new one. */
65 if (optix_denoiser_) {
66 optixDenoiserDestroy(optix_denoiser_);
67 }
68
69 /* Create OptiX denoiser handle on demand when it is first used. */
70 OptixDenoiserOptions denoiser_options = {};
71 denoiser_options.guideAlbedo = context.use_pass_albedo;
72 denoiser_options.guideNormal = context.use_pass_normal;
73
74 OptixDenoiserModelKind model = OPTIX_DENOISER_MODEL_KIND_AOV;
75 if (context.use_pass_motion) {
76 model = OPTIX_DENOISER_MODEL_KIND_TEMPORAL;
77 }
78
79 const OptixResult result = optixDenoiserCreate(
80 static_cast<OptiXDevice *>(denoiser_device_)->context,
81 model,
82 &denoiser_options,
83 &optix_denoiser_);
84
85 if (result != OPTIX_SUCCESS) {
86 set_error("Failed to create OptiX denoiser");
87 return false;
88 }
89
90 /* OptiX denoiser handle was created with the requested number of input passes. */
91 use_pass_albedo_ = context.use_pass_albedo;
92 use_pass_normal_ = context.use_pass_normal;
93 use_pass_motion_ = context.use_pass_motion;
94
95 /* OptiX denoiser has been created, but it needs configuration. */
96 is_configured_ = false;
97
98 return true;
99}
100
101bool OptiXDenoiser::denoise_configure_if_needed(DenoiseContext &context)
102{
103 /* Limit maximum tile size denoiser can be invoked with. */
104 const int2 tile_size = make_int2(min(context.buffer_params.width, 4096),
105 min(context.buffer_params.height, 4096));
106
107 if (is_configured_ && (configured_size_.x == tile_size.x && configured_size_.y == tile_size.y)) {
108 return true;
109 }
110
111 optix_device_assert(
112 denoiser_device_,
113 optixDenoiserComputeMemoryResources(optix_denoiser_, tile_size.x, tile_size.y, &sizes_));
114
115 const bool tiled = tile_size.x < context.buffer_params.width ||
116 tile_size.y < context.buffer_params.height;
117
118 /* Allocate denoiser state if tile size has changed since last setup. */
119 state_.device = denoiser_device_;
120 state_.alloc_to_device(sizes_.stateSizeInBytes + sizes_.withOverlapScratchSizeInBytes);
121
122 /* Initialize denoiser state for the current tile size. */
123 const OptixResult result = optixDenoiserSetup(
124 optix_denoiser_,
125 0, /* Work around bug in r495 drivers that causes artifacts when denoiser setup is called
126 * on a stream that is not the default stream. */
127 tile_size.x + (tiled ? sizes_.overlapWindowSizeInPixels * 2 : 0),
128 tile_size.y + (tiled ? sizes_.overlapWindowSizeInPixels * 2 : 0),
129 state_.device_pointer,
130 sizes_.stateSizeInBytes,
131 state_.device_pointer + sizes_.stateSizeInBytes,
132 sizes_.withOverlapScratchSizeInBytes);
133 if (result != OPTIX_SUCCESS) {
134 set_error("Failed to set up OptiX denoiser");
135 return false;
136 }
137
138 cuda_device_assert(denoiser_device_, cuCtxSynchronize());
139
140 is_configured_ = true;
141 configured_size_ = tile_size;
142
143 return true;
144}
145
146bool OptiXDenoiser::denoise_run(const DenoiseContext &context, const DenoisePass &pass)
147{
148 const BufferParams &buffer_params = context.buffer_params;
149 const int width = buffer_params.width;
150 const int height = buffer_params.height;
151
152 /* Set up input and output layer information. */
153 OptixImage2D color_layer = {0};
154 OptixImage2D albedo_layer = {0};
155 OptixImage2D normal_layer = {0};
156 OptixImage2D flow_layer = {0};
157
158 OptixImage2D output_layer = {0};
159 OptixImage2D prev_output_layer = {0};
160
161 /* Color pass. */
162 {
163 const int pass_denoised = pass.denoised_offset;
164 const int64_t pass_stride_in_bytes = context.buffer_params.pass_stride * sizeof(float);
165
166 color_layer.data = context.render_buffers->buffer.device_pointer +
167 pass_denoised * sizeof(float);
168 color_layer.width = width;
169 color_layer.height = height;
170 color_layer.rowStrideInBytes = pass_stride_in_bytes * context.buffer_params.stride;
171 color_layer.pixelStrideInBytes = pass_stride_in_bytes;
172 color_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3;
173 }
174
175 /* Previous output. */
176 if (context.prev_output.offset != PASS_UNUSED) {
177 const int64_t pass_stride_in_bytes = context.prev_output.pass_stride * sizeof(float);
178
179 prev_output_layer.data = context.prev_output.device_pointer +
180 context.prev_output.offset * sizeof(float);
181 prev_output_layer.width = width;
182 prev_output_layer.height = height;
183 prev_output_layer.rowStrideInBytes = pass_stride_in_bytes * context.prev_output.stride;
184 prev_output_layer.pixelStrideInBytes = pass_stride_in_bytes;
185 prev_output_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3;
186 }
187
188 /* Optional albedo and color passes. */
189 if (context.num_input_passes > 1) {
190 const device_ptr d_guiding_buffer = context.guiding_params.device_pointer;
191 const int64_t pixel_stride_in_bytes = context.guiding_params.pass_stride * sizeof(float);
192 const int64_t row_stride_in_bytes = context.guiding_params.stride * pixel_stride_in_bytes;
193
194 if (context.use_pass_albedo) {
195 albedo_layer.data = d_guiding_buffer + context.guiding_params.pass_albedo * sizeof(float);
196 albedo_layer.width = width;
197 albedo_layer.height = height;
198 albedo_layer.rowStrideInBytes = row_stride_in_bytes;
199 albedo_layer.pixelStrideInBytes = pixel_stride_in_bytes;
200 albedo_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3;
201 }
202
203 if (context.use_pass_normal) {
204 normal_layer.data = d_guiding_buffer + context.guiding_params.pass_normal * sizeof(float);
205 normal_layer.width = width;
206 normal_layer.height = height;
207 normal_layer.rowStrideInBytes = row_stride_in_bytes;
208 normal_layer.pixelStrideInBytes = pixel_stride_in_bytes;
209 normal_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3;
210 }
211
212 if (context.use_pass_motion) {
213 flow_layer.data = d_guiding_buffer + context.guiding_params.pass_flow * sizeof(float);
214 flow_layer.width = width;
215 flow_layer.height = height;
216 flow_layer.rowStrideInBytes = row_stride_in_bytes;
217 flow_layer.pixelStrideInBytes = pixel_stride_in_bytes;
218 flow_layer.format = OPTIX_PIXEL_FORMAT_FLOAT2;
219 }
220 }
221
222 /* Denoise in-place of the noisy input in the render buffers. */
223 output_layer = color_layer;
224
225 OptixDenoiserGuideLayer guide_layers = {};
226 guide_layers.albedo = albedo_layer;
227 guide_layers.normal = normal_layer;
228 guide_layers.flow = flow_layer;
229
230 OptixDenoiserLayer image_layers = {};
231 image_layers.input = color_layer;
232 image_layers.previousOutput = prev_output_layer;
233 image_layers.output = output_layer;
234
235 /* Finally run denoising. */
236 OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */
237
238 optix_device_assert(denoiser_device_,
239 optixUtilDenoiserInvokeTiled(
240 optix_denoiser_,
241 static_cast<OptiXDeviceQueue *>(denoiser_queue_.get())->stream(),
242 &params,
243 state_.device_pointer,
244 sizes_.stateSizeInBytes,
245 &guide_layers,
246 &image_layers,
247 1,
248 state_.device_pointer + sizes_.stateSizeInBytes,
249 sizes_.withOverlapScratchSizeInBytes,
250 sizes_.overlapWindowSizeInPixels,
251 configured_size_.x,
252 configured_size_.y));
253
254 return true;
255}
256
258
259#endif
unsigned int uint
return true
long long int int64_t
NODE_DECLARE int width
Definition buffers.h:70
bool denoise_buffer(const BufferParams &buffer_params, RenderBuffers *render_buffers, const int num_samples, bool allow_inplace_modification) override
DenoiserTypeMask denoisers
DeviceType type
nullptr float
@ DENOISER_OPTIX
Definition denoise.h:12
#define PASS_UNUSED
#define CCL_NAMESPACE_END
@ DEVICE_MASK_OPTIX
@ DEVICE_OPTIX
ccl_device_forceinline int2 make_int2(const int x, const int y)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int context(const bContext *C, const char *member, bContextDataResult *result)
#define min(a, b)
Definition sort.cc:36
uint64_t device_ptr
Definition types_base.h:44