Blender V5.0
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_hash.hh"
10
11#include "BKE_lib_id.hh"
12#include "BKE_mask.h"
13
14#include "DNA_ID.h"
15#include "DNA_mask_types.h"
16
17#include "COM_cached_mask.hh"
18#include "COM_context.hh"
19#include "COM_result.hh"
20#include "COM_utilities.hh"
21
22namespace blender::compositor {
23
24/* --------------------------------------------------------------------
25 * Cached Mask Key.
26 */
27
40
46
48{
49 return a.size == b.size && a.aspect_ratio == b.aspect_ratio && a.use_feather == b.use_feather &&
50 a.motion_blur_samples == b.motion_blur_samples &&
51 a.motion_blur_shutter == b.motion_blur_shutter;
52}
53
54/* --------------------------------------------------------------------
55 * Cached Mask.
56 */
57
59 int2 size,
60 int current_frame,
61 bool use_feather,
62 int motion_blur_samples,
63 float motion_blur_shutter)
64{
66
67 if (!mask) {
68 return handles;
69 }
70
71 /* If motion blur samples are 1, that means motion blur is disabled, in that case, just return
72 * the currently evaluated raster handle. */
73 if (motion_blur_samples == 1) {
75 BKE_maskrasterize_handle_init(handle, mask, size.x, size.y, true, true, use_feather);
76 handles.append(handle);
77 return handles;
78 }
79
80 /* Otherwise, we have a number of motion blur samples, so make a copy of the Mask ID and evaluate
81 * it at the different motion blur frames to get the needed raster handles. */
82 Mask *evaluation_mask = reinterpret_cast<Mask *>(
84
85 /* We evaluate at the frames in the range [current_frame - shutter, current_frame + shutter]. */
86 const float start_frame = current_frame - motion_blur_shutter;
87 const float frame_step = (motion_blur_shutter * 2.0f) / motion_blur_samples;
88 for (int i = 0; i < motion_blur_samples; i++) {
90 BKE_mask_evaluate(evaluation_mask, start_frame + frame_step * i, true);
92 handle, evaluation_mask, size.x, size.y, true, true, use_feather);
93 handles.append(handle);
94 }
95
96 BKE_id_free(nullptr, &evaluation_mask->id);
97
98 return handles;
99}
100
102 Mask *mask,
103 int2 size,
104 int frame,
105 float aspect_ratio,
106 bool use_feather,
107 int motion_blur_samples,
108 float motion_blur_shutter)
109 : result(context.create_result(ResultType::Float))
110{
112 mask, size, frame, use_feather, motion_blur_samples, motion_blur_shutter);
113
114 this->result.allocate_texture(size, false, ResultStorageType::CPU);
115 parallel_for(size, [&](const int2 texel) {
116 /* Compute the coordinates in the [0, 1] range and add 0.5 to evaluate the mask at the
117 * center of pixels. */
118 float2 coordinates = (float2(texel) + 0.5f) / float2(size);
119 /* Do aspect ratio correction around the center 0.5 point. */
120 coordinates = (coordinates - float2(0.5)) * float2(1.0, aspect_ratio) + float2(0.5);
121
122 float mask_value = 0.0f;
123 for (MaskRasterHandle *handle : handles) {
124 mask_value += BKE_maskrasterize_handle_sample(handle, coordinates);
125 }
126 this->result.store_pixel(texel, mask_value / handles.size());
127 });
128
129 for (MaskRasterHandle *handle : handles) {
131 }
132
133 if (context.use_gpu()) {
134 const Result gpu_result = this->result.upload_to_gpu(false);
135 this->result.release();
136 this->result = gpu_result;
137 }
138}
139
141{
142 this->result.release();
143}
144
145/* --------------------------------------------------------------------
146 * Cached Mask Container.
147 */
148
150{
151 /* First, delete all cached masks that are no longer needed. */
152 for (auto &cached_masks_for_id : map_.values()) {
153 cached_masks_for_id.remove_if([](auto item) { return !item.value->needed; });
154 }
155 map_.remove_if([](auto item) { return item.value.is_empty(); });
156 update_counts_.remove_if([&](auto item) { return !map_.contains(item.key); });
157
158 /* Second, reset the needed status of the remaining cached masks to false to ready them to track
159 * their needed status for the next evaluation. */
160 for (auto &cached_masks_for_id : map_.values()) {
161 for (auto &value : cached_masks_for_id.values()) {
162 value->needed = false;
163 }
164 }
165}
166
168 Mask *mask,
169 int2 size,
170 float aspect_ratio,
171 bool use_feather,
172 int motion_blur_samples,
173 float motion_blur_shutter)
174{
175 const CachedMaskKey key(
176 size, aspect_ratio, use_feather, motion_blur_samples, motion_blur_shutter);
177
178 const std::string library_key = mask->id.lib ? mask->id.lib->id.name : "";
179 const std::string id_key = std::string(mask->id.name) + library_key;
180 auto &cached_masks_for_id = map_.lookup_or_add_default(id_key);
181
182 /* Invalidate the cache for that mask if it was changed since it was cached. */
183 if (!cached_masks_for_id.is_empty() &&
184 mask->runtime.last_update != update_counts_.lookup(id_key))
185 {
186 cached_masks_for_id.clear();
187 }
188
189 auto &cached_mask = *cached_masks_for_id.lookup_or_add_cb(key, [&]() {
190 return std::make_unique<CachedMask>(context,
191 mask,
192 size,
193 context.get_frame_number(),
194 aspect_ratio,
195 use_feather,
196 motion_blur_samples,
197 motion_blur_shutter);
198 });
199
200 /* Store the current update count to later compare to and check if the mask changed. */
201 update_counts_.add_overwrite(id_key, mask->runtime.last_update);
202
203 cached_mask.needed = true;
204 return cached_mask.result;
205}
206
207} // namespace blender::compositor
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:777
@ LIB_ID_COPY_LOCALIZE
@ LIB_ID_COPY_NO_ANIMDATA
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.
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