Blender V4.3
depsgraph_light_linking.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
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_hash.hh"
15#include "BLI_listbase.h"
16#include "BLI_map.hh"
17#include "BLI_utildefines.h"
18
19#include "BKE_collection.hh"
20
22#include "DNA_layer_types.h"
23#include "DNA_object_types.h"
24#include "DNA_scene_types.h"
25
28
29#include "intern/depsgraph.hh"
30
31namespace deg = blender::deg;
32
33/* -------------------------------------------------------------------- */
38
39void eval_runtime_data(const ::Depsgraph *depsgraph, Object &object_eval)
40{
41 const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
42 deg_graph->light_linking_cache.eval_runtime_data(object_eval);
43}
44
45} // namespace blender::deg::light_linking
46
49/* -------------------------------------------------------------------- */
53namespace {
54
55/* TODO(sergey): Move to a public API, solving the const-correctness. */
56template<class T> static inline const T *get_original(const T *id)
57{
58 if (!id) {
59 return nullptr;
60 }
61 return reinterpret_cast<T *>(DEG_get_original_id(const_cast<ID *>(&id->id)));
62}
63
64/* Check whether the ID is suitable to be an input of the dependency graph. */
65/* TODO(sergey): Move the function and check to a more generic place. */
66#ifndef NDEBUG
67bool is_valid_input_id(const ID &id)
68{
69 return (id.tag & ID_TAG_LOCALIZED) || DEG_is_original_id(&id);
70}
71#endif
72
73} // namespace
74
76
82
83namespace internal {
84
85namespace {
86
87/* Helper class which takes care of allocating an unique light set IDs, performing checks for
88 * overflows. */
89class LightSetIDManager {
91
92 public:
93 explicit LightSetIDManager(const Scene &scene) : scene_(scene) {}
94
95 bool get(const LightSet &light_set, uint64_t &id)
96 {
97 /* Performance note.
98 *
99 * Always ensure the light set data exists in the map, even when an overflow happens. This has
100 * a downside of potentially higher memory usage and when there are many emitters with light
101 * linking, but it avoids distinct lookup + add fore the normal cases. */
102
103 const uint64_t light_set_id = light_set_id_map_.lookup_or_add_cb(light_set, [&]() {
104 const uint64_t new_light_set_id = next_light_set_id_++;
105
106 if (new_light_set_id == LightSet::MAX_ID + 1) {
107 printf("Maximum number of light linking sets (%d) exceeded scene \"%s\".\n",
109 scene_.id.name + 2);
110 }
111
112 return new_light_set_id;
113 });
114
115 id = light_set_id;
116
117 return id <= LightSet::MAX_ID;
118 }
119
120 private:
121 const Scene &scene_;
122
123 /* Next unique ID of a light set. */
124 uint64_t next_light_set_id_ = LightSet::DEFAULT_ID + 1;
125
126 /* Map from a link set to its original id. */
127 Map<internal::LightSet, uint64_t> light_set_id_map_;
128};
129
130} // namespace
131
132/* LightSet */
133
134bool LightSet::operator==(const LightSet &other) const
135{
136 return include_collection_mask == other.include_collection_mask &&
137 exclude_collection_mask == other.exclude_collection_mask;
138}
139
145
146/* EmitterSetMembership */
147
149{
150 const uint64_t effective_included_mask = included_sets_mask ? included_sets_mask :
152 return effective_included_mask & ~excluded_sets_mask;
153}
154
155/* EmitterDataMap. */
156
158{
159 emitter_data_map_.clear();
160 next_collection_id_ = 0;
161}
162
164{
165 BLI_assert(is_valid_input_id(emitter.id));
166
167 const Collection *collection = get_collection(emitter);
168 BLI_assert(collection);
169
170 /* Performance note.
171 *
172 * Always ensure the emitter data exists in the map, even when an overflow happens. This has a
173 * downside of potentially higher memory usage when there are many emitters with light linking,
174 * but it avoids distinct lookup + add fore the normal cases.
175 *
176 * On the API level the function always returns nullptr on overflow, so it is more of an internal
177 * behavior. */
178
179 EmitterData &emitter_data = emitter_data_map_.lookup_or_add_cb(collection, [&]() {
180 const uint64_t collection_id = next_collection_id_++;
181
182 EmitterData new_emitter_data;
183
184 if (collection_id > EmitterData::MAX_COLLECTION_ID) {
185 if (collection_id == EmitterData::MAX_COLLECTION_ID + 1) {
186 printf("Maximum number of light linking collections (%d) exceeded in scene \"%s\".\n",
188 scene.id.name + 2);
189 }
190 new_emitter_data.collection_mask = 0;
191 }
192 else {
193 new_emitter_data.collection_mask = uint64_t(1) << collection_id;
194 }
195
196 return new_emitter_data;
197 });
198
199 if (!emitter_data.collection_mask) {
200 return nullptr;
201 }
202
203 return &emitter_data;
204}
205
206const EmitterData *EmitterDataMap::get_data(const Object &emitter) const
207{
208 const Collection *collection_eval = get_collection(emitter);
209
210 if (!collection_eval) {
211 return nullptr;
212 }
213
214 const Collection *collection_orig = get_original(collection_eval);
215
216 return emitter_data_map_.lookup_ptr(collection_orig);
217}
218
219bool EmitterDataMap::can_skip_emitter(const Object &emitter) const
220{
221 BLI_assert(is_valid_input_id(emitter.id));
222
223 const Collection *collection = get_collection(emitter);
224
225 if (!collection) {
226 return true;
227 }
228
229 return emitter_data_map_.contains(collection);
230}
231
232/* LinkingData */
233
235{
236 light_linked_sets_.clear();
237 object_light_sets_.clear();
238}
239
240void LinkingData::link_object(const EmitterData &emitter_data,
241 const eCollectionLightLinkingState link_state,
242 const Object &object)
243{
244 LightSet &light_set = ensure_light_set_for(object);
245
246 switch (link_state) {
248 light_set.include_collection_mask |= emitter_data.collection_mask;
249 light_set.exclude_collection_mask &= ~emitter_data.collection_mask;
250 break;
251
253 light_set.exclude_collection_mask |= emitter_data.collection_mask;
254 light_set.include_collection_mask &= ~emitter_data.collection_mask;
255 break;
256 }
257}
258
259LightSet &LinkingData::ensure_light_set_for(const Object &object)
260{
261 BLI_assert(is_valid_input_id(object.id));
262
263 return light_linked_sets_.lookup_or_add_as(&object);
264}
265
266void LinkingData::clear_after_build()
267{
268 light_linked_sets_.clear_and_shrink();
269}
270
271void LinkingData::end_build(const Scene &scene, EmitterDataMap &emitter_data_map)
272{
273 LightSetIDManager light_set_id_manager(scene);
274
275 for (const auto it : light_linked_sets_.items()) {
276 const Object *receiver = it.key;
277 LightSet &light_set = it.value;
278
279 uint64_t light_set_id;
280 if (!light_set_id_manager.get(light_set, light_set_id)) {
281 continue;
282 }
283
284 const uint64_t light_set_mask = uint64_t(1) << light_set_id;
285
286 object_light_sets_.add(receiver, light_set_id);
287
288 update_emitters_membership(emitter_data_map, light_set, light_set_mask);
289 }
290
291 clear_after_build();
292}
293
294void LinkingData::update_emitters_membership(EmitterDataMap &emitter_data_map,
295 const LightSet &light_set,
296 const uint64_t light_set_mask)
297{
298 for (EmitterData &emitter_data : emitter_data_map.values()) {
299 EmitterSetMembership &set_membership = get_emitter_set_membership(emitter_data);
300
301 if (emitter_data.collection_mask & light_set.include_collection_mask) {
302 set_membership.included_sets_mask |= light_set_mask;
303 }
304 if (emitter_data.collection_mask & light_set.exclude_collection_mask) {
305 set_membership.excluded_sets_mask |= light_set_mask;
306 }
307 }
308}
309
311{
312 const Object *object_orig = get_original(&object);
313 return object_light_sets_.lookup_default(object_orig, LightSet::DEFAULT_ID);
314}
315
316} // namespace internal
317
318namespace {
319
320/* Iterate over all objects of the collection and invoke the given callback with two arguments:
321 * the given collection light linking settings, and the object (passed as reference).
322 *
323 * Note that if an object is reachable from multiple children collection the callback is invoked
324 * for all of them. */
325template<class Proc>
326static void foreach_light_collection_object_inner(
327 const CollectionLightLinking &collection_light_linking,
328 const Collection &collection,
329 Proc &&callback)
330{
331 LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
332 foreach_light_collection_object_inner(
333 collection_light_linking, *collection_child->collection, callback);
334 }
335
336 LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
337 callback(collection_light_linking, *collection_object->ob);
338 }
339}
340
341/* Iterate over all objects of the collection and invoke the given callback with two arguments:
342 * CollectionLightLinking and the actual Object (passed as reference).
343 *
344 * The CollectionLightLinking denotes the effective light linking settings of the object. It comes
345 * from the first level of hierarchy from the given collection.
346 *
347 * Note that if an object is reachable from multiple children collection the callback is invoked
348 * for all of them. */
349template<class Proc>
350static void foreach_light_collection_object(const Collection &collection, Proc &&callback)
351{
352 LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
353 foreach_light_collection_object_inner(
354 collection_child->light_linking, *collection_child->collection, callback);
355 }
356
357 LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
358 callback(collection_object->light_linking, *collection_object->ob);
359 }
360}
361
362} // namespace
363
365{
366 light_emitter_data_map_.clear();
367 shadow_emitter_data_map_.clear();
368
369 light_linking_.clear();
370 shadow_linking_.clear();
371}
372
373void Cache::add_emitter(const Scene &scene, const Object &emitter)
374{
375 BLI_assert(is_valid_input_id(emitter.id));
376
377 add_light_linking_emitter(scene, emitter);
378 add_shadow_linking_emitter(scene, emitter);
379}
380
381void Cache::add_light_linking_emitter(const Scene &scene, const Object &emitter)
382{
383 BLI_assert(is_valid_input_id(emitter.id));
384
385 if (light_emitter_data_map_.can_skip_emitter(emitter)) {
386 return;
387 }
388
389 const EmitterData *light_emitter_data = light_emitter_data_map_.ensure_data_if_possible(scene,
390 emitter);
391 if (light_emitter_data) {
392 foreach_light_collection_object(
394 [&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
395 add_receiver_object(*light_emitter_data, collection_light_linking, receiver);
396 });
397 }
398}
399
400void Cache::add_shadow_linking_emitter(const Scene &scene, const Object &emitter)
401{
402 BLI_assert(is_valid_input_id(emitter.id));
403
404 if (shadow_emitter_data_map_.can_skip_emitter(emitter)) {
405 return;
406 }
407
408 const EmitterData *shadow_emitter_data = shadow_emitter_data_map_.ensure_data_if_possible(
409 scene, emitter);
410 if (shadow_emitter_data) {
411 foreach_light_collection_object(
413 [&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
414 add_blocker_object(*shadow_emitter_data, collection_light_linking, receiver);
415 });
416 }
417}
418
419void Cache::add_receiver_object(const EmitterData &emitter_data,
420 const CollectionLightLinking &collection_light_linking,
421 const Object &receiver)
422{
423 BLI_assert(is_valid_input_id(receiver.id));
424
425 light_linking_.link_object(
426 emitter_data, eCollectionLightLinkingState(collection_light_linking.link_state), receiver);
427}
428
429void Cache::add_blocker_object(const EmitterData &emitter_data,
430 const CollectionLightLinking &collection_light_linking,
431 const Object &blocker)
432{
433 BLI_assert(is_valid_input_id(blocker.id));
434
435 shadow_linking_.link_object(
436 emitter_data, eCollectionLightLinkingState(collection_light_linking.link_state), blocker);
437}
438
439void Cache::end_build(const Scene &scene)
440{
441 if (!has_light_linking()) {
442 return;
443 }
444
445 light_linking_.end_build(scene, light_emitter_data_map_);
446 shadow_linking_.end_build(scene, shadow_emitter_data_map_);
447}
448
449void Cache::eval_runtime_data(Object &object_eval) const
450{
451 static const LightLinkingRuntime runtime_no_links = {
453
454 if (!has_light_linking()) {
455 /* No light linking used in the scene, still reset to default on objects that have
456 * allocated light linking data structures since we can't free them here. */
457 if (object_eval.light_linking) {
458 object_eval.light_linking->runtime = runtime_no_links;
459 }
460
461 return;
462 }
463
464 /* Receiver and blocker configuration. */
465 LightLinkingRuntime runtime = {};
466 runtime.receiver_light_set = light_linking_.get_light_set_for(object_eval);
467 runtime.blocker_shadow_set = shadow_linking_.get_light_set_for(object_eval);
468
469 /* Emitter configuration. */
470 const EmitterData *light_emitter_data = light_emitter_data_map_.get_data(object_eval);
471 if (light_emitter_data) {
472 runtime.light_set_membership = light_emitter_data->light_membership.get_mask();
473 }
474 else {
476 }
477
478 const EmitterData *shadow_emitter_data = shadow_emitter_data_map_.get_data(object_eval);
479 if (shadow_emitter_data) {
480 runtime.shadow_set_membership = shadow_emitter_data->shadow_membership.get_mask();
481 }
482 else {
484 }
485
486 const bool need_runtime = (memcmp(&runtime, &runtime_no_links, sizeof(runtime)) != 0);
487
488 /* Assign, allocating light linking on demand if needed. */
489 if (object_eval.light_linking) {
490 object_eval.light_linking->runtime = runtime;
491 if (!need_runtime) {
492 /* Note that this will only remove lazily allocated light_linking on the evaluated object,
493 * as an empty light_linking is not allowed on the original object. */
495 }
496 }
497 else if (need_runtime) {
498 object_eval.light_linking = MEM_cnew<LightLinking>(__func__);
499 object_eval.light_linking->runtime = runtime;
500 }
501}
502
503} // namespace blender::deg::light_linking
504
void BKE_light_linking_free_if_empty(struct Object *object)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
bool DEG_is_original_id(const ID *id)
ID * DEG_get_original_id(ID *id)
@ ID_TAG_LOCALIZED
Definition DNA_ID.h:954
Object groups, one object can be in many groups at once.
eCollectionLightLinkingState
@ COLLECTION_LIGHT_LINKING_STATE_EXCLUDE
@ COLLECTION_LIGHT_LINKING_STATE_INCLUDE
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
void clear()
Definition BLI_map.hh:989
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:582
bool contains(const Key &key) const
Definition BLI_map.hh:329
void eval_runtime_data(Object &object_eval) const
void add_emitter(const Scene &scene, const Object &emitter)
EmitterData * ensure_data_if_possible(const Scene &scene, const Object &emitter)
const EmitterData * get_data(const Object &emitter) const
void end_build(const Scene &scene, EmitterDataMap &emitter_data_map)
void link_object(const EmitterData &emitter_data, eCollectionLightLinkingState link_state, const Object &object)
#define printf
const Depsgraph * depsgraph
DEGForeachIDComponentCallback callback
void eval_runtime_data(const ::Depsgraph *depsgraph, Object &object_eval)
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
unsigned __int64 uint64_t
Definition stdint.h:90
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
struct Collection * blocker_collection
LightLinkingRuntime runtime
struct Collection * receiver_collection
LightLinking * light_linking
light_linking::Cache light_linking_cache
Definition depsgraph.hh:176