Blender V5.0
denoised_auxiliary_pass.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#ifdef WITH_OPENIMAGEDENOISE
6
7# include <cstdint>
8# include <memory>
9
10# include "BLI_assert.h"
11# include "BLI_hash.hh"
12# include "BLI_span.hh"
13
14# include "MEM_guardedalloc.h"
15
16# include "GPU_state.hh"
17# include "GPU_texture.hh"
18
19# include "COM_context.hh"
21# include "COM_result.hh"
22# include "COM_utilities_oidn.hh"
23
24# include <OpenImageDenoise/oidn.hpp>
25
26namespace blender::compositor {
27
28/* ------------------------------------------------------------------------------------------------
29 * Denoised Auxiliary Pass Key.
30 */
31
32DenoisedAuxiliaryPassKey::DenoisedAuxiliaryPassKey(const DenoisedAuxiliaryPassType type,
33 const oidn::Quality quality)
34 : type(type), quality(quality)
35{
36}
37
38uint64_t DenoisedAuxiliaryPassKey::hash() const
39{
40 return get_default_hash(this->type, this->quality);
41}
42
43bool operator==(const DenoisedAuxiliaryPassKey &a, const DenoisedAuxiliaryPassKey &b)
44{
45 return a.type == b.type && a.quality == b.quality;
46}
47
48/* --------------------------------------------------------------------
49 * Denoised Auxiliary Pass.
50 */
51
52/* A callback to cancel the filter operations by evaluating the context's is_canceled method. The
53 * API specifies that true indicates the filter should continue, while false indicates it should
54 * stop, so invert the condition. This callback can also be used to track progress using the given
55 * n argument, but we currently don't make use of it. See OIDNProgressMonitorFunction in the API
56 * for more information. */
57static bool oidn_progress_monitor_function(void *user_ptr, double /*n*/)
58{
59 const Context *context = static_cast<const Context *>(user_ptr);
60 return !context->is_canceled();
61}
62
63static const char *get_pass_name(const DenoisedAuxiliaryPassType type)
64{
65 switch (type) {
66 case DenoisedAuxiliaryPassType::Albedo:
67 return "albedo";
68 case DenoisedAuxiliaryPassType::Normal:
69 return "normal";
70 }
71
73 return "";
74}
75
76DenoisedAuxiliaryPass::DenoisedAuxiliaryPass(Context &context,
77 const Result &pass,
78 const DenoisedAuxiliaryPassType type,
79 const oidn::Quality quality)
80{
81 /* Assign the pass data to the denoised buffer since we will be denoising in place. */
82 if (context.use_gpu()) {
84 this->denoised_buffer = static_cast<float *>(GPU_texture_read(pass, GPU_DATA_FLOAT, 0));
85 }
86 else {
87 this->denoised_buffer = static_cast<float *>(MEM_dupallocN(pass.cpu_data().data()));
88 }
89
90 const int width = pass.domain().size.x;
91 const int height = pass.domain().size.y;
92
93 /* Float3 results might be stored in 4-component textures due to hardware limitations, so we
94 * need to use the pixel stride of the texture. */
95 const int channels_count = context.use_gpu() ?
97 pass.channels_count();
98 const int pixel_stride = sizeof(float) * channels_count;
99
100 oidn::DeviceRef device = create_oidn_device(context);
101 device.commit();
102
103 const int64_t buffer_size = int64_t(width) * height * channels_count;
104 const MutableSpan<float> buffer_span = MutableSpan<float>(this->denoised_buffer, buffer_size);
105 oidn::BufferRef buffer = create_oidn_buffer(device, buffer_span);
106
107 /* Denoise the pass in place, so set it to both the input and output. */
108 oidn::FilterRef filter = device.newFilter("RT");
109 const char *pass_name = get_pass_name(type);
110 filter.setImage(pass_name, buffer, oidn::Format::Float3, width, height, 0, pixel_stride);
111 filter.setImage("output", buffer, oidn::Format::Float3, width, height, 0, pixel_stride);
112 filter.set("quality", quality);
113 filter.setProgressMonitorFunction(oidn_progress_monitor_function, &context);
114 filter.commit();
115 filter.execute();
116
117 if (buffer.getStorage() != oidn::Storage::Host) {
118 buffer.read(0, buffer_size * sizeof(float), this->denoised_buffer);
119 }
120}
121
122DenoisedAuxiliaryPass::~DenoisedAuxiliaryPass()
123{
124 MEM_freeN(this->denoised_buffer);
125}
126
127/* --------------------------------------------------------------------
128 * Denoised Auxiliary Pass Container.
129 */
130
131DenoisedAuxiliaryPass &DenoisedAuxiliaryPassContainer::get(Context &context,
132 const Result &pass,
133 const DenoisedAuxiliaryPassType type,
134 const oidn::Quality quality)
135{
136 const DenoisedAuxiliaryPassKey key(type, quality);
137
138 return *map_.lookup_or_add_cb(key, [&]() {
139 return std::make_unique<DenoisedAuxiliaryPass>(context, pass, type, quality);
140 });
141}
142
143} // namespace blender::compositor
144
145#endif
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
@ GPU_BARRIER_TEXTURE_UPDATE
Definition GPU_state.hh:39
void GPU_memory_barrier(GPUBarrier barrier)
Definition gpu_state.cc:326
size_t GPU_texture_component_len(blender::gpu::TextureFormat format)
blender::gpu::TextureFormat GPU_texture_format(const blender::gpu::Texture *texture)
@ GPU_DATA_FLOAT
void * GPU_texture_read(blender::gpu::Texture *texture, eGPUDataFormat data_format, int mip_level)
Read Guarded memory(de)allocation.
bool operator==(const AssetWeakReference &a, const AssetWeakReference &b)
long long int int64_t
unsigned long long int uint64_t
nullptr float
#define filter
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
int context(const bContext *C, const char *member, bContextDataResult *result)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233