Files
test/source/blender/blenkernel/BKE_collection.h
Bastien Montagne 4fe4a52cd7 Fix missing tagging of collections in some BKE_collections codepath.
In particular, while Object (un)linking was already tagged in relevant
BKE code, collection (un)linking was not in several cases.

This was (partially) done by user code, though almost never for the
whole hierarchy of parents.

Technically, the tag is done as part of
`collection_object_cache_free_parent_recursive`/`collection_object_cache_free`,
since currently clearing this cache is done everytime to collection
hierarchy or their content is modified.

It also removes `collection_tag_update_parent_recursive`, which was
already doing something similar, but was only called from code
adding/removing objects to collections, and was walking the same parent
hierarchy as `collection_object_cache_free_parent_recursive`.

This commit implements the decision made in #116601, to tag modified
data as close as possible from the code modifying it.

---------------------

This has an impact on deg tagging, which takes over twice as much cycles
with this commit compared to previous code  when opening a Pets production
file with several liboverrides (`deggraph_id_tag_update_single_flag` goes
from less than 0.03% to over 0.06%).

The overhead remains extremely low though, and is totally unmeasurable in
global execution timing. Timing of the liboverride processing on opening
the production file also did not show any measurable differences.

Pull Request: https://projects.blender.org/blender/blender/pulls/116986
2024-01-12 16:24:22 +01:00

463 lines
17 KiB
C

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include "BLI_compiler_compat.h"
#include "BLI_ghash.h"
#include "BLI_iterator.h"
#include "BLI_sys_types.h"
#include "DNA_listBase.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Structs */
struct BLI_Iterator;
struct Base;
struct BlendDataReader;
struct BlendLibReader;
struct BlendWriter;
struct Collection;
struct Depsgraph;
struct ID;
struct Library;
struct Main;
struct Object;
struct Scene;
struct ViewLayer;
typedef struct CollectionParent {
struct CollectionParent *next, *prev;
struct Collection *collection;
} CollectionParent;
/* Collections */
/**
* Add a collection to a collection ListBase and synchronize all render layers
* The ListBase is NULL when the collection is to be added to the master collection
*/
struct Collection *BKE_collection_add(struct Main *bmain,
struct Collection *parent,
const char *name);
/**
* Add \a collection_dst to all scene collections that reference object \a ob_src is in.
* Used to replace an instance object with a collection (library override operator).
*
* Logic is very similar to #BKE_collection_object_add_from().
*/
void BKE_collection_add_from_object(struct Main *bmain,
struct Scene *scene,
const struct Object *ob_src,
struct Collection *collection_dst);
/**
* Add \a collection_dst to all scene collections that reference collection \a collection_src is
* in.
*
* Logic is very similar to #BKE_collection_object_add_from().
*/
void BKE_collection_add_from_collection(struct Main *bmain,
struct Scene *scene,
struct Collection *collection_src,
struct Collection *collection_dst);
/**
* Free (or release) any data used by this collection (does not free the collection itself).
*/
void BKE_collection_free_data(struct Collection *collection);
/**
* Remove a collection, optionally removing its child objects or moving
* them to parent collections.
*/
bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy);
/**
* Make a deep copy (aka duplicate) of the given collection and all of its children, recursively.
*
* \warning This functions will clear all \a bmain #ID.idnew pointers, unless \a
* #LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on, in which case caller is
* responsible to reconstruct collection dependencies information's
* (i.e. call #BKE_main_collection_sync).
*/
struct Collection *BKE_collection_duplicate(struct Main *bmain,
struct Collection *parent,
struct Collection *collection,
uint duplicate_flags,
uint duplicate_options);
/* Master Collection for Scene */
#define BKE_SCENE_COLLECTION_NAME "Scene Collection"
struct Collection *BKE_collection_master_add(struct Scene *scene);
/* Collection Objects */
bool BKE_collection_has_object(struct Collection *collection, const struct Object *ob);
bool BKE_collection_has_object_recursive(struct Collection *collection, struct Object *ob);
bool BKE_collection_has_object_recursive_instanced(struct Collection *collection,
struct Object *ob);
struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
struct Object *ob);
bool BKE_collection_is_empty(const struct Collection *collection);
/**
* Add object to given collection, ensuring this collection is 'editable' (i.e. local and not a
* liboverride), and finding a suitable parent one otherwise.
*/
bool BKE_collection_object_add(struct Main *bmain,
struct Collection *collection,
struct Object *ob);
/**
* Add object to given collection, similar to #BKE_collection_object_add.
*
* However, it additionally ensures that the selected collection is also part of the given
* `view_layer`, if non-NULL. Otherwise, the object is not added to any collection.
*/
bool BKE_collection_viewlayer_object_add(struct Main *bmain,
const struct ViewLayer *view_layer,
struct Collection *collection,
struct Object *ob);
/**
* Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection.
*
* NOTE: required in certain cases, like do-versioning or complex ID management tasks.
*/
bool BKE_collection_object_add_notest(struct Main *bmain,
struct Collection *collection,
struct Object *ob);
/**
* Add \a ob_dst to all scene collections that reference object \a ob_src is in.
* Used for copying objects.
*
* Logic is very similar to #BKE_collection_add_from_object()
*/
void BKE_collection_object_add_from(struct Main *bmain,
struct Scene *scene,
struct Object *ob_src,
struct Object *ob_dst);
/**
* Remove object from collection.
*/
bool BKE_collection_object_remove(struct Main *bmain,
struct Collection *collection,
struct Object *object,
bool free_us);
/**
* Replace one object with another in a collection (managing user counts).
*/
bool BKE_collection_object_replace(struct Main *bmain,
struct Collection *collection,
struct Object *ob_old,
struct Object *ob_new);
/**
* Move object from a collection into another
*
* If source collection is NULL move it from all the existing collections.
*/
void BKE_collection_object_move(struct Main *bmain,
struct Scene *scene,
struct Collection *collection_dst,
struct Collection *collection_src,
struct Object *ob);
/**
* Remove object from all collections of scene
*/
bool BKE_scene_collections_object_remove(struct Main *bmain,
struct Scene *scene,
struct Object *object,
bool free_us);
/**
* Check all collections in \a bmain (including embedded ones in scenes) for invalid
* CollectionObject (either with NULL object pointer, or duplicates), and remove them.
*
* \note In case of duplicates, the first CollectionObject in the list is kept, all others are
* removed.
*/
void BKE_collections_object_remove_invalids(struct Main *bmain);
/**
* Remove all NULL children from parent collections of changed \a collection.
* This is used for library remapping, where these pointers have been set to NULL.
* Otherwise this should never happen.
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
* \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
* \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
void BKE_collections_child_remove_nulls(struct Main *bmain,
struct Collection *parent_collection,
struct Collection *child_collection);
/* Dependencies. */
bool BKE_collection_is_in_scene(struct Collection *collection);
void BKE_collections_after_lib_link(struct Main *bmain);
bool BKE_collection_object_cyclic_check(struct Main *bmain,
struct Object *object,
struct Collection *collection);
/* Object list cache. */
struct ListBase BKE_collection_object_cache_get(struct Collection *collection);
ListBase BKE_collection_object_cache_instanced_get(struct Collection *collection);
/** Free the object cache of given `collection` and all of its ancestors (recursively).
*
* \param bmain: The Main database owning the collection. May be `nullptr`, only used if doing
* depsgraph tagging.
* \param id_create_flag: Flags controlling ID creation, used here to enable or
* not depsgraph tagging of affected IDs (e.g. #LIB_ID_CREATE_NO_DEG_TAG would prevent depsgraph
* tagging). */
void BKE_collection_object_cache_free(const struct Main *bmain,
struct Collection *collection,
const int id_create_flag);
/**
* Free the object cache of all collections in given `bmain`, including master collections of
* scenes.
*/
void BKE_main_collections_object_cache_free(const struct Main *bmain);
struct Base *BKE_collection_or_layer_objects(const struct Scene *scene,
struct ViewLayer *view_layer,
struct Collection *collection);
/* Editing. */
/**
* Return Scene Collection for a given index.
*
* The index is calculated from top to bottom counting the children before the siblings.
*/
struct Collection *BKE_collection_from_index(struct Scene *scene, int index);
/**
* The automatic/fallback name of a new collection.
*/
void BKE_collection_new_name_get(struct Collection *collection_parent, char *rname);
/**
* The name to show in the interface.
*/
const char *BKE_collection_ui_name_get(struct Collection *collection);
/**
* Select all the objects in this Collection (and its nested collections) for this ViewLayer.
* Return true if any object was selected.
*/
bool BKE_collection_objects_select(const struct Scene *scene,
struct ViewLayer *view_layer,
struct Collection *collection,
bool deselect);
/* Collection children */
bool BKE_collection_child_add(struct Main *bmain,
struct Collection *parent,
struct Collection *child);
bool BKE_collection_child_add_no_sync(struct Main *bmain,
struct Collection *parent,
struct Collection *child);
bool BKE_collection_child_remove(struct Main *bmain,
struct Collection *parent,
struct Collection *child);
bool BKE_collection_move(struct Main *bmain,
struct Collection *to_parent,
struct Collection *from_parent,
struct Collection *relative,
bool relative_after,
struct Collection *collection);
/**
* Find potential cycles in collections.
*
* \param new_ancestor: the potential new owner of given \a collection,
* or the collection to check if the later is NULL.
* \param collection: the collection we want to add to \a new_ancestor,
* may be NULL if we just want to ensure \a new_ancestor does not already have cycles.
* \return true if a cycle is found.
*/
bool BKE_collection_cycle_find(struct Collection *new_ancestor, struct Collection *collection);
/**
* Find and fix potential cycles in collections.
*
* \param collection: The collection to check for existing cycles.
* \return true if cycles are found and fixed.
*/
bool BKE_collection_cycles_fix(struct Main *bmain, struct Collection *collection);
bool BKE_collection_has_collection(const struct Collection *parent,
const struct Collection *collection);
/**
* Rebuild parent relationships from child ones, for all children of given \a collection.
*
* \note Given collection is assumed to already have valid parents.
*/
void BKE_collection_parent_relations_rebuild(struct Collection *collection);
/**
* Rebuild parent relationships from child ones, for all collections in given \a bmain.
*/
void BKE_main_collections_parent_relations_rebuild(struct Main *bmain);
/**
* Perform some validation on integrity of the data of this collection.
*
* \return `true` if everything is OK, false if some errors are detected. */
bool BKE_collection_validate(struct Collection *collection);
/* .blend file I/O */
/**
* Perform some pre-writing cleanup on the COllection data itself (_not_ in any sub-data
* referenced by pointers). To be called before writing the Collection struct itself.
*/
void BKE_collection_blend_write_prepare_nolib(struct BlendWriter *writer,
struct Collection *collection);
void BKE_collection_blend_write_nolib(struct BlendWriter *writer, struct Collection *collection);
void BKE_collection_blend_read_data(struct BlendDataReader *reader,
struct Collection *collection,
struct ID *owner_id);
/* Iteration callbacks. */
typedef void (*BKE_scene_objects_Cb)(struct Object *ob, void *data);
typedef void (*BKE_scene_collections_Cb)(struct Collection *ob, void *data);
/* Iteration over objects in collection. */
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode) \
{ \
int _base_flag = (_mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER; \
int _object_visibility_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_HIDE_VIEWPORT : \
OB_HIDE_RENDER; \
int _base_id = 0; \
for (Base *_base = (Base *)BKE_collection_object_cache_get(_collection).first; _base; \
_base = _base->next, _base_id++) \
{ \
Object *_object = _base->object; \
if ((_base->flag & _base_flag) && \
(_object->visibility_flag & _object_visibility_flag) == 0) {
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END \
} \
} \
} \
((void)0)
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object) \
for (Base *_base = (Base *)BKE_collection_object_cache_get(_collection).first; _base; \
_base = _base->next) \
{ \
Object *_object = _base->object; \
BLI_assert(_object != NULL);
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END \
} \
((void)0)
/* Iteration over collections in scene. */
/**
* Only use this in non-performance critical situations
* (it iterates over all scene collections twice)
*/
void BKE_scene_collections_iterator_begin(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_collections_iterator_next(struct BLI_Iterator *iter);
void BKE_scene_collections_iterator_end(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_begin(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_end(struct BLI_Iterator *iter);
/**
* Iterate over objects in the scene based on a flag.
*
* \note The object->flag is tested against flag.
*/
typedef struct SceneObjectsIteratorExData {
struct Scene *scene;
int flag;
void *iter_data;
} SceneObjectsIteratorExData;
void BKE_scene_objects_iterator_begin_ex(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next_ex(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_end_ex(struct BLI_Iterator *iter);
/**
* Generate a new #GSet (or extend given `objects_gset` if not NULL) with all objects referenced by
* all collections of given `scene`.
*
* \note This will include objects without a base currently
* (because they would belong to excluded collections only e.g.).
*/
struct GSet *BKE_scene_objects_as_gset(struct Scene *scene, struct GSet *objects_gset);
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_collections_iterator_begin, \
BKE_scene_collections_iterator_next, \
BKE_scene_collections_iterator_end, \
scene, \
Collection *, \
_instance)
#define FOREACH_SCENE_COLLECTION_END ITER_END
#define FOREACH_COLLECTION_BEGIN(_bmain, _scene, Type, _instance) \
{ \
Type _instance; \
Collection *_instance_next; \
bool is_scene_collection = (_scene) != NULL; \
\
if (_scene) { \
_instance_next = _scene->master_collection; \
} \
else { \
_instance_next = (Collection *)(_bmain)->collections.first; \
} \
\
while ((_instance = _instance_next)) { \
if (is_scene_collection) { \
_instance_next = (Collection *)(_bmain)->collections.first; \
is_scene_collection = false; \
} \
else { \
_instance_next = (Collection *)_instance->id.next; \
}
#define FOREACH_COLLECTION_END \
} \
} \
((void)0)
#define FOREACH_SCENE_OBJECT_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_objects_iterator_begin, \
BKE_scene_objects_iterator_next, \
BKE_scene_objects_iterator_end, \
scene, \
Object *, \
_instance)
#define FOREACH_SCENE_OBJECT_END ITER_END
#ifdef __cplusplus
}
#endif