diff options
author | Bastien Montagne <bastien@blender.org> | 2022-08-12 13:40:46 +0300 |
---|---|---|
committer | Bastien Montagne <bastien@blender.org> | 2022-08-12 13:40:46 +0300 |
commit | 74645d969b57b3cf6ca14ec7a218345aea311f16 (patch) | |
tree | 7868f499c56176c5d7bd57416ec29caf65e0de4d | |
parent | 7f733e294d5485178df800685c69cc15547ee73c (diff) | |
parent | afd2e9ebc38a6182746707c6c6ba65ad7414d377 (diff) |
Merge branch 'blender-v3.3-release'
Conflicts:
source/blender/blenkernel/BKE_lib_override.h
15 files changed, 602 insertions, 48 deletions
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index ec8d6f76a0b..95f4c8f6dce 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -85,7 +85,11 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data); -typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, struct ID *id); +/** \param owner_id_hint: If non-NULL, a potential owner of the given embedded ID. Can speed up + * look-up of the owner ID in some cases. */ +typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, + struct ID *id, + struct ID *owner_id_hint); typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, struct ID *id, diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index d1eb6e01ae3..38469fd1b8e 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -60,16 +60,18 @@ void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, bool do_ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_id_user); /** - * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the actual ID owning + * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the acutal ID owning * it. * * \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master * collection or root node tree, or a shape key). * - * \param r_owner_id: If given, will be set with the actual ID owning the return liboverride data. + * \param owner_id_hint If not NULL, a potential owner for the given override-embedded `id`. + * \param r_owner_id If given, will be set with the actual ID owning the return liboverride data. */ IDOverrideLibrary *BKE_lib_override_library_get(struct Main *bmain, struct ID *id, + struct ID *owner_id_hint, struct ID **r_owner_id); /** diff --git a/source/blender/blenkernel/BKE_lib_override.h.orig b/source/blender/blenkernel/BKE_lib_override.h.orig new file mode 100644 index 00000000000..ee90f0676f7 --- /dev/null +++ b/source/blender/blenkernel/BKE_lib_override.h.orig @@ -0,0 +1,510 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2016 Blender Foundation. All rights reserved. */ + +#pragma once + +/** \file + * \ingroup bke + * + * API to manage data-blocks inside of Blender's Main data-base, or as independent runtime-only + * data. + * + * \note `BKE_lib_` files are for operations over data-blocks themselves, although they might + * alter Main as well (when creating/renaming/deleting an ID e.g.). + * + * \section Function Names + * + * \warning Descriptions below is ideal goal, current status of naming does not yet fully follow it + * (this is WIP). + * + * - `BKE_lib_override_library_` should be used for function affecting a single ID. + * - `BKE_lib_override_library_main_` should be used for function affecting the whole collection + * of IDs in a given Main data-base. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct BlendFileReadReport; +struct Collection; +struct ID; +struct IDOverrideLibrary; +struct IDOverrideLibraryProperty; +struct IDOverrideLibraryPropertyOperation; +struct Library; +struct Main; +struct Object; +struct PointerRNA; +struct PropertyRNA; +struct ReportList; +struct Scene; +struct ViewLayer; + +/** + * Initialize empty overriding of \a reference_id by \a local_id. + */ +struct IDOverrideLibrary *BKE_lib_override_library_init(struct ID *local_id, + struct ID *reference_id); +/** + * Shallow or deep copy of a whole override from \a src_id to \a dst_id. + */ +void BKE_lib_override_library_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy); +/** + * Clear any overriding data from given \a override. + */ +void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, bool do_id_user); +/** + * Free given \a override. + */ +void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_id_user); + +/** + * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the actual ID owning + * it. + * + * \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master + * collection or root node tree, or a shape key). + * +<<<<<<< HEAD + * \param r_owner_id: If given, will be set with the actual ID owning the return liboverride data. +======= + * \param owner_id_hint If not NULL, a potential owner for the given override-embedded `id`. + * \param r_owner_id If given, will be set with the actual ID owning the return liboverride data. +>>>>>>> blender-v3.3-release + */ +IDOverrideLibrary *BKE_lib_override_library_get(struct Main *bmain, + struct ID *id, + struct ID *owner_id_hint, + struct ID **r_owner_id); + +/** + * Check if given ID has some override rules that actually indicate the user edited it. + */ +bool BKE_lib_override_library_is_user_edited(const struct ID *id); + +/** + * Check if given ID is a system override. + */ +bool BKE_lib_override_library_is_system_defined(const struct Main *bmain, const struct ID *id); + +/** + * Check if given Override Property for given ID is animated (through a F-Curve in an Action, or + * from a driver). + * + * \param override_rna_prop: if not NULL, the RNA property matching the given path in the + * `override_prop`. + * \param rnaprop_index: Array in the RNA property, 0 if unknown or irrelevant. + */ +bool BKE_lib_override_library_property_is_animated(const ID *id, + const IDOverrideLibraryProperty *override_prop, + const struct PropertyRNA *override_rna_prop, + const int rnaprop_index); + +/** + * Check if given ID is a leaf in its liboverride hierarchy (i.e. if it does not use any other + * override ID). + * + * NOTE: Embedded IDs of override IDs are not considered as leaves. + */ +bool BKE_lib_override_library_is_hierarchy_leaf(struct Main *bmain, struct ID *id); + +/** + * Create an overridden local copy of linked reference. + * + * \note This function is very basic, low-level. It does not consider any hierarchical dependency, + * and also prevents any automatic re-sync of this local override. + */ +struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, + struct ID *reference_id, + bool do_tagged_remap); +/** + * Create overridden local copies of all tagged data-blocks in given Main. + * + * \note Set `id->newid` of overridden libs with newly created overrides, + * caller is responsible to clean those pointers before/after usage as needed. + * + * \note By default, it will only remap newly created local overriding data-blocks between + * themselves, to avoid 'enforcing' those overrides into all other usages of the linked data in + * main. You can add more local IDs to be remapped to use new overriding ones by setting their + * LIB_TAG_DOIT tag. + * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * + * \param id_root_reference: the linked ID that is considered as the root of the overridden + * hierarchy. + * + * \param id_hierarchy_root: the override ID that is the root of the hierarchy. May be NULL, in + * which case it is assumed that the given `id_root_reference` is tagged for override, and its + * newly created override will be used as hierarchy root. Must be NULL if + * `id_hierarchy_root_reference` is not NULL. + * + * \param id_hierarchy_root_reference: the linked ID that is the root of the hierarchy. Must be + * tagged for override. May be NULL, in which case it is assumed that the given `id_root_reference` + * is tagged for override, and its newly created override will be used as hierarchy root. Must be + * NULL if `id_hierarchy_root` is not NULL. + * + * \param do_no_main: Create the new override data outside of Main database. + * Used for resyncing of linked overrides. + * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * + * \return \a true on success, \a false otherwise. + */ +bool BKE_lib_override_library_create_from_tag(struct Main *bmain, + struct Library *owner_library, + const struct ID *id_root_reference, + struct ID *id_hierarchy_root, + const struct ID *id_hierarchy_root_reference, + bool do_no_main, + const bool do_fully_editable); +/** + * Advanced 'smart' function to create fully functional overrides. + * + * \note Currently it only does special things if given \a id_root is an object or collection, more + * specific behaviors may be added in the future for other ID types. + * + * \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at + * its beginning, so caller code can add extra data-blocks to be overridden as well. + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + * + * \param owner_library: the library in which the overrides should be created. Besides versioning + * and resync code path, this should always be NULL (i.e. the local .blend file). + * + * \param id_root_reference: The linked root ID to create an override from. May be a sub-root of + * the overall hierarchy, in which case calling code is expected to have already tagged required + * 'path' of IDs leading from the given `id_hierarchy_root` to the given `id_root`. + * + * \param id_hierarchy_root_reference: The ID to be used a hierarchy root of the overrides to be + * created. Can be either the linked root ID of the whole override hierarchy, (typically the same + * as `id_root`, unless a sub-part only of the hierarchy is overridden), or the already existing + * override hierarchy root if part of the hierarchy is already overridden. + * + * \param id_instance_hint: Some ID used as hint/reference to do some post-processing after + * overrides have been created, may be NULL. Typically, the Empty object instantiating the linked + * collection we override, currently. + * + * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. + * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * + * \return true if override was successfully created. + */ +bool BKE_lib_override_library_create(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct Library *owner_library, + struct ID *id_root_reference, + struct ID *id_hierarchy_root_reference, + struct ID *id_instance_hint, + struct ID **r_id_root_override, + const bool do_fully_editable); +/** + * Create a library override template. + */ +bool BKE_lib_override_library_template_create(struct ID *id); +/** + * Convert a given proxy object into a library override. + * + * \note This is a thin wrapper around \a BKE_lib_override_library_create, only extra work is to + * actually convert the proxy itself into an override first. + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + * \return true if override was successfully created. + */ +bool BKE_lib_override_library_proxy_convert(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct Object *ob_proxy); +/** + * Convert all proxy objects into library overrides. + * + * \note Only affects local proxies, linked ones are not affected. + */ +void BKE_lib_override_library_main_proxy_convert(struct Main *bmain, + struct BlendFileReadReport *reports); + +/** + * Find and set the 'hierarchy root' ID pointer of all library overrides in given `bmain`. + * + * NOTE: Cannot be called from `do_versions_after_linking` as this code needs a single complete + * Main database, not a split-by-libraries one. + */ +void BKE_lib_override_library_main_hierarchy_root_ensure(struct Main *bmain); + +/** + * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked + * data, from an existing override hierarchy. + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + * \param id_root: The root liboverride ID to resync from. + * \return true if override was successfully resynced. + */ +bool BKE_lib_override_library_resync(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct ID *id_root, + struct Collection *override_resync_residual_storage, + bool do_hierarchy_enforce, + struct BlendFileReadReport *reports); +/** + * Detect and handle required resync of overrides data, when relations between reference linked IDs + * have changed. + * + * This is a fairly complex and costly operation, typically it should be called after + * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases. + * + * This function will first detect the remaining cases requiring a resync (namely, either when an + * existing linked ID that did not require to be overridden before now would be, or when new IDs + * are added to the hierarchy). + * + * Then it will handle the resync of necessary IDs (through calls to + * #BKE_lib_override_library_resync). + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + */ +void BKE_lib_override_library_main_resync(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct BlendFileReadReport *reports); + +/** + * Advanced 'smart' function to delete library overrides (including their existing override + * hierarchy) and remap their usages to their linked reference IDs. + * + * \note All IDs tagged with #LIB_TAG_DOIT will be deleted. + * + * \param id_root: The root liboverride ID to delete. + */ +void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root); + +/** + * Make given ID fully local. + * + * \note Only differs from lower-level #BKE_lib_override_library_free in infamous embedded ID + * cases. + */ +void BKE_lib_override_library_make_local(struct ID *id); + +/** + * Find override property from given RNA path, if it exists. + */ +struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find( + struct IDOverrideLibrary *override, const char *rna_path); +/** + * Find override property from given RNA path, or create it if it does not exist. + */ +struct IDOverrideLibraryProperty *BKE_lib_override_library_property_get( + struct IDOverrideLibrary *override, const char *rna_path, bool *r_created); +/** + * Remove and free given \a override_property from given ID \a override. + */ +void BKE_lib_override_library_property_delete(struct IDOverrideLibrary *override, + struct IDOverrideLibraryProperty *override_property); +/** + * Get the RNA-property matching the \a library_prop override property. Used for UI to query + * additional data about the overridden property (e.g. UI name). + * + * \param idpoin: Pointer to the override ID. + * \param library_prop: The library override property to find the matching RNA property for. + * \param r_index: The RNA array flat index (i.e. flattened index in case of multi-dimensional + * array properties). See #RNA_path_resolve_full family of functions for details. + */ +bool BKE_lib_override_rna_property_find(struct PointerRNA *idpoin, + const struct IDOverrideLibraryProperty *library_prop, + struct PointerRNA *r_override_poin, + struct PropertyRNA **r_override_prop, + int *r_index); + +/** + * Find override property operation from given sub-item(s), if it exists. + */ +struct IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find( + struct IDOverrideLibraryProperty *override_property, + const char *subitem_refname, + const char *subitem_locname, + int subitem_refindex, + int subitem_locindex, + bool strict, + bool *r_strict); +/** + * Find override property operation from given sub-item(s), or create it if it does not exist. + */ +struct IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_get( + struct IDOverrideLibraryProperty *override_property, + short operation, + const char *subitem_refname, + const char *subitem_locname, + int subitem_refindex, + int subitem_locindex, + bool strict, + bool *r_strict, + bool *r_created); +/** + * Remove and free given \a override_property_operation from given ID \a override_property. + */ +void BKE_lib_override_library_property_operation_delete( + struct IDOverrideLibraryProperty *override_property, + struct IDOverrideLibraryPropertyOperation *override_property_operation); + +/** + * Validate that required data for a given operation are available. + */ +bool BKE_lib_override_library_property_operation_operands_validate( + struct IDOverrideLibraryPropertyOperation *override_property_operation, + struct PointerRNA *ptr_dst, + struct PointerRNA *ptr_src, + struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_dst, + struct PropertyRNA *prop_src, + struct PropertyRNA *prop_storage); + +/** + * Check against potential \a bmain. + */ +void BKE_lib_override_library_validate(struct Main *bmain, + struct ID *id, + struct ReportList *reports); +/** + * Check against potential \a bmain. + */ +void BKE_lib_override_library_main_validate(struct Main *bmain, struct ReportList *reports); + +/** + * Check that status of local data-block is still valid against current reference one. + * + * It means that all overridable, but not overridden, properties' local values must be equal to + * reference ones. Clears #LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some property has been changed in local and a new + * #IDOverrideProperty (of #IDOverridePropertyOperation) has to be added. + * + * \return true if status is OK, false otherwise. + */ +bool BKE_lib_override_library_status_check_local(struct Main *bmain, struct ID *local); +/** + * Check that status of reference data-block is still valid against current local one. + * + * It means that all non-overridden properties' local values must be equal to reference ones. + * Clears LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some reference has changed and local + * needs to be updated against it. + * + * \return true if status is OK, false otherwise. + */ +bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct ID *local); + +/** + * Compare local and reference data-blocks and create new override operations as needed, + * or reset to reference values if overriding is not allowed. + * + * \note Defining override operations is only mandatory before saving a `.blend` file on disk + * (not for undo!). + * Knowing that info at runtime is only useful for UI/UX feedback. + * + * \note This is by far the biggest operation (the more time-consuming) of the three so far, + * since it has to go over all properties in depth (all overridable ones at least). + * Generating differential values and applying overrides are much cheaper. + * + * \return true if any library operation was created. + */ +bool BKE_lib_override_library_operations_create(struct Main *bmain, struct ID *local); +/** + * Check all overrides from given \a bmain and create/update overriding operations as needed. + */ +bool BKE_lib_override_library_main_operations_create(struct Main *bmain, bool force_auto); + +/** + * Reset all overrides in given \a id_root, while preserving ID relations. + * + * \param do_reset_system_override: If \a true, reset the given ID as a system override one (i.e. + * non-editable). + */ +void BKE_lib_override_library_id_reset(struct Main *bmain, + struct ID *id_root, + bool do_reset_system_override); +/** + * Reset all overrides in given \a id_root and its dependencies, while preserving ID relations. + * + * \param do_reset_system_override: If \a true, reset the given ID and all of its descendants in + * the override hierarchy as system override ones (i.e. non-editable). + */ +void BKE_lib_override_library_id_hierarchy_reset(struct Main *bmain, + struct ID *id_root, + bool do_reset_system_override); + +/** + * Set or clear given tag in all operations in that override property data. + */ +void BKE_lib_override_library_operations_tag(struct IDOverrideLibraryProperty *override_property, + short tag, + bool do_set); +/** + * Set or clear given tag in all properties and operations in that override data. + */ +void BKE_lib_override_library_properties_tag(struct IDOverrideLibrary *override, + short tag, + bool do_set); +/** + * Set or clear given tag in all properties and operations in that Main's ID override data. + */ +void BKE_lib_override_library_main_tag(struct Main *bmain, short tag, bool do_set); + +/** + * Remove all tagged-as-unused properties and operations from that ID override data. + */ +void BKE_lib_override_library_id_unused_cleanup(struct ID *local); +/** + * Remove all tagged-as-unused properties and operations from that Main's ID override data. + */ +void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain); + +/** + * Update given override from its reference (re-applying overridden properties). + */ +void BKE_lib_override_library_update(struct Main *bmain, struct ID *local); +/** + * Update all overrides from given \a bmain. + */ +void BKE_lib_override_library_main_update(struct Main *bmain); + +/** + * In case an ID is used by another liboverride ID, user may not be allowed to delete it. + */ +bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id); + +/* Storage (.blend file writing) part. */ + +/* For now, we just use a temp main list. */ +typedef struct Main OverrideLibraryStorage; + +/** + * Initialize an override storage. + */ +OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void); +/** + * Generate suitable 'write' data (this only affects differential override operations). + * + * Note that \a local ID is no more modified by this call, + * all extra data are stored in its temp \a storage_id copy. + */ +struct ID *BKE_lib_override_library_operations_store_start( + struct Main *bmain, OverrideLibraryStorage *override_storage, struct ID *local); +/** + * Restore given ID modified by #BKE_lib_override_library_operations_store_start, to its + * original state. + */ +void BKE_lib_override_library_operations_store_end(OverrideLibraryStorage *override_storage, + struct ID *local); +void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 934c3053a02..4967e3482c6 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -162,7 +162,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) } } -static ID *collection_owner_get(Main *bmain, ID *id) +static ID *collection_owner_get(Main *bmain, ID *id, ID *owner_id_hint) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { return id; @@ -172,6 +172,11 @@ static ID *collection_owner_get(Main *bmain, ID *id) Collection *master_collection = (Collection *)id; BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0); + if (owner_id_hint != NULL && GS(owner_id_hint->name) == ID_SCE && + ((Scene *)owner_id_hint)->master_collection == master_collection) { + return owner_id_hint; + } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->master_collection == master_collection) { return &scene->id; diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 07ce4e46e9b..461a6f15ca1 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -91,7 +91,7 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK); } -static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id) +static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id, ID *UNUSED(owner_id_hint)) { return ((Key *)id)->from; } diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 758a317e415..05a00fb54fd 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -94,6 +94,7 @@ BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src) /** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bmain, const ID *id, + const ID *owner_id_hint, const ID **r_owner_id) { if (r_owner_id != nullptr) { @@ -104,7 +105,8 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma if (id_type->owner_get != nullptr) { /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const * is okay. */ - const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id)); + const ID *owner_id = id_type->owner_get( + const_cast<Main *>(bmain), const_cast<ID *>(id), const_cast<ID *>(owner_id_hint)); if (r_owner_id != nullptr) { *r_owner_id = owner_id; } @@ -115,13 +117,17 @@ BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main *bma return id->override_library; } -IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain, ID *id, ID **r_owner_id) +IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain, + ID *id, + ID *owner_id_hint, + ID **r_owner_id) { /* Reuse the implementation of the const access function, which does not change the arguments. * Add const explicitly to make it clear to the compiler to avoid just calling this function. */ return const_cast<IDOverrideLibrary *>( BKE_lib_override_library_get(const_cast<const Main *>(bmain), const_cast<const ID *>(id), + const_cast<const ID *>(owner_id_hint), const_cast<const ID **>(r_owner_id))); } @@ -319,7 +325,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) { if (ID_IS_OVERRIDE_LIBRARY(id)) { const ID *override_owner_id; - BKE_lib_override_library_get(bmain, id, &override_owner_id); + BKE_lib_override_library_get(bmain, id, nullptr, &override_owner_id); return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) != 0; } @@ -1087,8 +1093,9 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * } const Library *reference_lib = - BKE_lib_override_library_get(bmain, id_owner, nullptr)->reference->lib; - const ID *to_id_reference = BKE_lib_override_library_get(bmain, to_id, nullptr)->reference; + BKE_lib_override_library_get(bmain, id_owner, nullptr, nullptr)->reference->lib; + const ID *to_id_reference = + BKE_lib_override_library_get(bmain, to_id, nullptr, nullptr)->reference; if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; @@ -1439,7 +1446,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); ID *id_owner; int best_level_placeholder = 0; - BKE_lib_override_library_get(bmain, id, &id_owner); + BKE_lib_override_library_get(bmain, id, nullptr, &id_owner); return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder); } /* This way we won't process again that ID, should we encounter it again through another @@ -1478,7 +1485,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); ID *id_owner; int best_level_placeholder = 0; - BKE_lib_override_library_get(bmain, best_root_id_candidate, &id_owner); + BKE_lib_override_library_get(bmain, best_root_id_candidate, nullptr, &id_owner); best_root_id_candidate = lib_override_root_find( bmain, id_owner, curr_level + 1, &best_level_placeholder); } @@ -1795,7 +1802,8 @@ static bool lib_override_library_resync(Main *bmain, /* While this should not happen in typical cases (and won't be properly supported here), * user is free to do all kind of very bad things, including having different local * overrides of a same linked ID in a same hierarchy. */ - IDOverrideLibrary *id_override_library = BKE_lib_override_library_get(bmain, id, nullptr); + IDOverrideLibrary *id_override_library = BKE_lib_override_library_get( + bmain, id, nullptr, nullptr); if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) { continue; @@ -2177,7 +2185,7 @@ static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id) if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->owner_get != nullptr) { - id = id_type->owner_get(bmain, id); + id = id_type->owner_get(bmain, id, nullptr); } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 7cc07d59187..38d1a30592d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -713,7 +713,7 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, /* Directly 'by-pass' to actual real ID owner. */ const IDTypeInfo *type_info_from = BKE_idtype_get_info_from_id(id_from); BLI_assert(type_info_from->owner_get != NULL); - id_from = type_info_from->owner_get(bmain, id_from); + id_from = type_info_from->owner_get(bmain, id_from, NULL); } lib_query_unused_ids_tag_recurse( diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 86c05d6d085..d50b8662f82 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -404,7 +404,7 @@ static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data) } } -static ID *node_owner_get(Main *bmain, ID *id) +static ID *node_owner_get(Main *bmain, ID *id, ID *owner_id_hint) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { return id; @@ -412,6 +412,12 @@ static ID *node_owner_get(Main *bmain, ID *id) /* TODO: Sort this NO_MAIN or not for embedded node trees. See T86119. */ // BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0); + bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id); + + if (owner_id_hint != nullptr && ntreeFromID(owner_id_hint) == ntree) { + return owner_id_hint; + } + ListBase *lists[] = {&bmain->materials, &bmain->lights, &bmain->worlds, @@ -421,7 +427,6 @@ static ID *node_owner_get(Main *bmain, ID *id) &bmain->simulations, nullptr}; - bNodeTree *ntree = (bNodeTree *)id; for (int i = 0; lists[i] != nullptr; i++) { LISTBASE_FOREACH (ID *, id_iter, lists[i]) { if (ntreeFromID(id_iter) == ntree) { diff --git a/source/blender/blenkernel/intern/outliner_treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c index 03c327bec2f..09e2baf2be1 100644 --- a/source/blender/blenkernel/intern/outliner_treehash.c +++ b/source/blender/blenkernel/intern/outliner_treehash.c @@ -21,11 +21,20 @@ typedef struct TseGroup { TreeStoreElem **elems; + /* Index of last used #TreeStoreElem item, to speed up search for another one. */ int lastused; + /* Counter used to reduce the amount of 'rests' of `lastused` index, otherwise search for unused + * item is exponential and becomes critically slow when there are a lot of items in the group. */ + int lastused_reset_count; + /* Number of items currently in use. */ int size; + /* Number of items currently allocated. */ int allocated; } TseGroup; +/* Only allow reset of #TseGroup.lastused counter to 0 once every 1k search. */ +#define TSEGROUP_LASTUSED_RESET_VALUE 10000 + /* Allocate structure for TreeStoreElements; * Most of elements in treestore have no duplicates, * so there is no need to preallocate memory for more than one pointer */ @@ -47,6 +56,7 @@ static void tse_group_add_element(TseGroup *tse_group, TreeStoreElem *elem) sizeof(TreeStoreElem *) * tse_group->allocated); } tse_group->elems[tse_group->size] = elem; + tse_group->lastused = tse_group->size; tse_group->size++; } @@ -136,6 +146,7 @@ void BKE_outliner_treehash_clear_used(void *treehash) GHASH_ITER (gh_iter, treehash) { TseGroup *group = BLI_ghashIterator_getValue(&gh_iter); group->lastused = 0; + group->lastused_reset_count = 0; } } @@ -157,7 +168,6 @@ void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem) *val_p = tse_group_create(); } group = *val_p; - group->lastused = 0; tse_group_add_element(group, elem); } @@ -204,7 +214,15 @@ TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash, int offset = group->lastused; for (int i = 0; i < size; i++, offset++) { + /* Once at the end of the array of items, in most cases it just means that all items are + * used, so only check the whole array once every TSEGROUP_LASTUSED_RESET_VALUE times. */ if (offset >= size) { + if (LIKELY(group->lastused_reset_count <= TSEGROUP_LASTUSED_RESET_VALUE)) { + group->lastused_reset_count++; + group->lastused = group->size - 1; + break; + } + group->lastused_reset_count = 0; offset = 0; } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index cb3d0923348..5813f1d090c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -663,7 +663,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, * system override with reset. */ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - BKE_lib_override_library_get(bmain, id, &id); + BKE_lib_override_library_get(bmain, id, NULL, &id); } if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) { id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 7d0a0a921e4..02d54e4f702 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -364,7 +364,7 @@ void outliner_collection_delete( const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - ID *scene_owner = id_type->owner_get(bmain, &parent->id); + ID *scene_owner = id_type->owner_get(bmain, &parent->id, NULL); BLI_assert(GS(scene_owner->name) == ID_SCE); if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) { skip = true; @@ -597,7 +597,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id); BLI_assert(id_type->owner_get != nullptr); - Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id); + Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id, NULL); BLI_assert(scene_owner != nullptr); BLI_assert(GS(scene_owner->id.name) == ID_SCE); diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index f8705c3f0ad..e0a1958795a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -15,6 +15,7 @@ #include "BLT_translation.h" +#include "BKE_lib_override.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -80,6 +81,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & class OverrideIDHierarchyBuilder { SpaceOutliner &space_outliner_; + Main &bmain_; MainIDRelations &id_relations_; struct HierarchyBuildData { @@ -93,8 +95,10 @@ class OverrideIDHierarchyBuilder { }; public: - OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations) - : space_outliner_(space_outliner), id_relations_(id_relations) + OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, + Main &bmain, + MainIDRelations &id_relations) + : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations) { } @@ -115,7 +119,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( * returning. */ BKE_main_relations_create(bmain, 0); - OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations); + OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations); /* Keep track over which ID base elements were already added, and expand them once added. */ Map<ID_Type, TreeElement *> id_base_te_map; @@ -165,7 +169,8 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id, static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, const ID &parent_id, FunctionRef<void(ID &)> fn); -static bool id_is_in_override_hierarchy(const ID &id, +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, const ID &relationship_parent_id, const ID &override_root_id); @@ -177,7 +182,11 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare build_data.parent_ids.add(&parent_id); foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) { - if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) { + /* Some IDs can use themselves, early abort. */ + if (&id == &parent_id) { + return; + } + if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) { return; } @@ -276,7 +285,8 @@ static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, } } -static bool id_is_in_override_hierarchy(const ID &id, +static bool id_is_in_override_hierarchy(const Main &bmain, + const ID &id, const ID &relationship_parent_id, const ID &override_root_id) { @@ -286,20 +296,12 @@ static bool id_is_in_override_hierarchy(const ID &id, const ID *real_override_id = &id; if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) { - /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no - * other ID directly uses the embedded one. Should be true, but the debug code adds some checks - * to validate this assumption. */ - real_override_id = &relationship_parent_id; - -#ifndef NDEBUG - if (GS(id.name) == ID_KE) { - const Key *key = (Key *)&id; - BLI_assert(real_override_id == key->from); - } - else { - BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0); - } -#endif + /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be + * drivers directly between an object and a shapekey). */ + BKE_lib_override_library_get(const_cast<Main *>(&bmain), + const_cast<ID *>(&id), + const_cast<ID *>(&relationship_parent_id), + const_cast<ID **>(&real_override_id)); } if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index bb638f8929d..ce6e18b62bd 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -50,7 +50,7 @@ #include "lineart_intern.h" typedef struct LineartIsecSingle { - float v1[3], v2[3]; + double v1[3], v2[3]; LineartTriangle *tri1, *tri2; } LineartIsecSingle; @@ -3285,8 +3285,8 @@ static void lineart_add_isec_thread(LineartIsecThread *th, th->array = new_array; } LineartIsecSingle *isec_single = &th->array[th->current]; - copy_v3fl_v3db(isec_single->v1, v1); - copy_v3fl_v3db(isec_single->v2, v2); + copy_v3_v3_db(isec_single->v1, v1); + copy_v3_v3_db(isec_single->v2, v2); isec_single->tri1 = tri1; isec_single->tri2 = tri2; if (tri1->target_reference > tri2->target_reference) { @@ -4582,8 +4582,8 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) LineartIsecSingle *is = &th->array[j]; LineartVert *v1 = v; LineartVert *v2 = v + 1; - copy_v3db_v3fl(v1->gloc, is->v1); - copy_v3db_v3fl(v2->gloc, is->v2); + copy_v3_v3_db(v1->gloc, is->v1); + copy_v3_v3_db(v2->gloc, is->v2); /* The intersection line has been generated only in geometry space, so we need to transform * them as well. */ mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc); diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc index 5d570657b53..f14e2113e13 100644 --- a/source/blender/makesrna/intern/rna_path.cc +++ b/source/blender/makesrna/intern/rna_path.cc @@ -940,7 +940,7 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) BLI_assert_msg(0, "Missing handling of embedded id type."); return id; } - return id_type->owner_get(bmain, id); + return id_type->owner_get(bmain, id, nullptr); } static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0f922ffc13c..2e78cc97099 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1478,7 +1478,7 @@ static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int va if (owner_id && GS(owner_id->name) == ID_NT) { /* For compositing nodes, find the corresponding scene. */ const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(owner_id); - owner_id = type_info->owner_get(G_MAIN, owner_id); + owner_id = type_info->owner_get(G_MAIN, owner_id, NULL); } if (owner_id && GS(owner_id->name) == ID_SCE) { BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id); |