Blender V4.5
cached_mask.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <cstdint>
6#include <memory>
7
8#include "BLI_array.hh"
9#include "BLI_hash.hh"
11
12#include "GPU_texture.hh"
13
14#include "BKE_lib_id.hh"
15#include "BKE_mask.h"
16
17#include "DNA_ID.h"
18#include "DNA_mask_types.h"
19
20#include "COM_cached_mask.hh"
21#include "COM_context.hh"
22#include "COM_result.hh"
23#include "COM_utilities.hh"
24
25namespace blender::compositor {
26
27/* --------------------------------------------------------------------
28 * Cached Mask Key.
29 */
30
43
49
51{
52 return a.size == b.size && a.aspect_ratio == b.aspect_ratio && a.use_feather == b.use_feather &&
53 a.motion_blur_samples == b.motion_blur_samples &&
54 a.motion_blur_shutter == b.motion_blur_shutter;
55}
56
57/* --------------------------------------------------------------------
58 * Cached Mask.
59 */
60
62 int2 size,
63 int current_frame,
64 bool use_feather,
65 int motion_blur_samples,
66 float motion_blur_shutter)
67{
69
70 if (!mask) {
71 return handles;
72 }
73
74 /* If motion blur samples are 1, that means motion blur is disabled, in that case, just return
75 * the currently evaluated raster handle. */
76 if (motion_blur_samples == 1) {
78 BKE_maskrasterize_handle_init(handle, mask, size.x, size.y, true, true, use_feather);
79 handles.append(handle);
80 return handles;
81 }
82
83 /* Otherwise, we have a number of motion blur samples, so make a copy of the Mask ID and evaluate
84 * it at the different motion blur frames to get the needed raster handles. */
85 Mask *evaluation_mask = reinterpret_cast<Mask *>(
87
88 /* We evaluate at the frames in the range [current_frame - shutter, current_frame + shutter]. */
89 const float start_frame = current_frame - motion_blur_shutter;
90 const float frame_step = (motion_blur_shutter * 2.0f) / motion_blur_samples;
91 for (int i = 0; i < motion_blur_samples; i++) {
93 BKE_mask_evaluate(evaluation_mask, start_frame + frame_step * i, true);
95 handle, evaluation_mask, size.x, size.y, true, true, use_feather);
96 handles.append(handle);
97 }
98
99 BKE_id_free(nullptr, &evaluation_mask->id);
100
101 return handles;
102}
103
105 Mask *mask,
106 int2 size,
107 int frame,
108 float aspect_ratio,
109 bool use_feather,
110 int motion_blur_samples,
111 float motion_blur_shutter)
112 : result(context.create_result(ResultType::Float))
113{
115 mask, size, frame, use_feather, motion_blur_samples, motion_blur_shutter);
116
117 evaluated_mask_ = Array<float>(size.x * size.y);
118 parallel_for(size, [&](const int2 texel) {
119 /* Compute the coordinates in the [0, 1] range and add 0.5 to evaluate the mask at the
120 * center of pixels. */
121 float2 coordinates = (float2(texel) + 0.5f) / float2(size);
122 /* Do aspect ratio correction around the center 0.5 point. */
123 coordinates = (coordinates - float2(0.5)) * float2(1.0, aspect_ratio) + float2(0.5);
124
125 float mask_value = 0.0f;
126 for (MaskRasterHandle *handle : handles) {
127 mask_value += BKE_maskrasterize_handle_sample(handle, coordinates);
128 }
129 evaluated_mask_[texel.y * size.x + texel.x] = mask_value / handles.size();
130 });
131
132 for (MaskRasterHandle *handle : handles) {
134 }
135
136 if (context.use_gpu()) {
137 this->result.allocate_texture(Domain(size), false);
138 GPU_texture_update(this->result, GPU_DATA_FLOAT, evaluated_mask_.data());
139
140 /* CPU-side data no longer needed, so free it. */
141 evaluated_mask_ = Array<float>();
142 }
143 else {
144 this->result.wrap_external(evaluated_mask_.data(), size);
145 }
146}
147
149{
150 this->result.release();
151}
152
153/* --------------------------------------------------------------------
154 * Cached Mask Container.
155 */
156
158{
159 /* First, delete all cached masks that are no longer needed. */
160 for (auto &cached_masks_for_id : map_.values()) {
161 cached_masks_for_id.remove_if([](auto item) { return !item.value->needed; });
162 }
163 map_.remove_if([](auto item) { return item.value.is_empty(); });
164 update_counts_.remove_if([&](auto item) { return !map_.contains(item.key); });
165
166 /* Second, reset the needed status of the remaining cached masks to false to ready them to track
167 * their needed status for the next evaluation. */
168 for (auto &cached_masks_for_id : map_.values()) {
169 for (auto &value : cached_masks_for_id.values()) {
170 value->needed = false;
171 }
172 }
173}
174
176 Mask *mask,
177 int2 size,
178 float aspect_ratio,
179 bool use_feather,
180 int motion_blur_samples,
181 float motion_blur_shutter)
182{
183 const CachedMaskKey key(
184 size, aspect_ratio, use_feather, motion_blur_samples, motion_blur_shutter);
185
186 const std::string library_key = mask->id.lib ? mask->id.lib->id.name : "";
187 const std::string id_key = std::string(mask->id.name) + library_key;
188 auto &cached_masks_for_id = map_.lookup_or_add_default(id_key);
189
190 /* Invalidate the cache for that mask if it was changed since it was cached. */
191 if (!cached_masks_for_id.is_empty() &&
192 mask->runtime.last_update != update_counts_.lookup(id_key))
193 {
194 cached_masks_for_id.clear();
195 }
196
197 auto &cached_mask = *cached_masks_for_id.lookup_or_add_cb(key, [&]() {
198 return std::make_unique<CachedMask>(context,
199 mask,
200 size,
201 context.get_frame_number(),
202 aspect_ratio,
203 use_feather,
204 motion_blur_samples,
205 motion_blur_shutter);
206 });
207
208 /* Store the current update count to later compare to and check if the mask changed. */
209 update_counts_.add_overwrite(id_key, mask->runtime.last_update);
210
211 cached_mask.needed = true;
212 return cached_mask.result;
213}
214
215} // namespace blender::compositor
@ LIB_ID_COPY_LOCALIZE
@ LIB_ID_COPY_NO_ANIMDATA
void BKE_id_free(Main *bmain, void *idv)
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:767
void BKE_maskrasterize_handle_free(MaskRasterHandle *mr_handle)
float BKE_maskrasterize_handle_sample(MaskRasterHandle *mr_handle, const float xy[2])
MaskRasterHandle * BKE_maskrasterize_handle_new(void)
void BKE_mask_evaluate(struct Mask *mask, float ctime, bool do_newframe)
void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, struct Mask *mask, int width, int height, bool do_aspect_correct, bool do_mask_aa, bool do_feather)
ID and Library types, which are fundamental for SDNA.
@ GPU_DATA_FLOAT
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
Result & get(Context &context, Mask *mask, int2 size, float aspect_ratio, bool use_feather, int motion_blur_samples, float motion_blur_shutter)
CachedMaskKey(int2 size, float aspect_ratio, bool use_feather, int motion_blur_samples, float motion_blur_shutter)
CachedMask(Context &context, Mask *mask, int2 size, int frame, float aspect_ratio, bool use_feather, int motion_blur_samples, float motion_blur_shutter)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b)
void parallel_for(const int2 range, const Function &function)
static Vector< MaskRasterHandle * > get_mask_raster_handles(Mask *mask, int2 size, int current_frame, bool use_feather, int motion_blur_samples, float motion_blur_shutter)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
i
Definition text_draw.cc:230
ParamHandle ** handles