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