diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-10-19 15:29:43 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-10-19 15:32:08 +0300 |
commit | 417847c161ee9fa461c4f354c6b779b566c23796 (patch) | |
tree | d8f06ce5f930a1a1ae7a5a5295703241e5e78f78 /source/blender/blenkernel/intern/library_query.c | |
parent | 82f842c27f4ff483be0fda6c02d3a4527cc20ff2 (diff) |
Fix T49775: Appending data with internal dependency cycles prevents correct clearing of linked data-blocks.
This is not a simple fix, but imho still needs to be backported to 2.78a...
Diffstat (limited to 'source/blender/blenkernel/intern/library_query.c')
-rw-r--r-- | source/blender/blenkernel/intern/library_query.c | 112 |
1 files changed, 91 insertions, 21 deletions
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 08f9c439a7a..cec7fbacd80 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -1029,30 +1029,32 @@ static int foreach_libblock_id_users_callback(void *user_data, ID *self_id, ID * { IDUsersIter *iter = user_data; - /* XXX This is actually some kind of hack... - * Issue is, shapekeys' 'from' ID pointer is not actually ID usage. - * Maybe we should even nuke it from BKE_library_foreach_ID_link, not 100% sure yet... - */ - if ((GS(self_id->name) == ID_KE) && (((Key *)self_id)->from == *id_p)) { - return IDWALK_RET_NOP; - } - /* XXX another hack, for similar reasons as above one. */ - if ((GS(self_id->name) == ID_OB) && (((Object *)self_id)->proxy_from == (Object *)*id_p)) { - return IDWALK_RET_NOP; - } + if (*id_p) { + /* XXX This is actually some kind of hack... + * Issue is, shapekeys' 'from' ID pointer is not actually ID usage. + * Maybe we should even nuke it from BKE_library_foreach_ID_link, not 100% sure yet... + */ + if ((GS(self_id->name) == ID_KE) && (((Key *)self_id)->from == *id_p)) { + return IDWALK_RET_NOP; + } + /* XXX another hack, for similar reasons as above one. */ + if ((GS(self_id->name) == ID_OB) && (((Object *)self_id)->proxy_from == (Object *)*id_p)) { + return IDWALK_RET_NOP; + } - if (*id_p && (*id_p == iter->id)) { + if (*id_p == iter->id) { #if 0 - printf("%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, indirect_usage: %d)\n", - iter->curr_id->name, iter->id->name, (cb_flag & IDWALK_USER) ? 1 : 0, (cb_flag & IDWALK_USER_ONE) ? 1 : 0, - (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0, - (cb_flag & IDWALK_INDIRECT_USAGE) ? 1 : 0); + printf("%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, indirect_usage: %d)\n", + iter->curr_id->name, iter->id->name, (cb_flag & IDWALK_USER) ? 1 : 0, (cb_flag & IDWALK_USER_ONE) ? 1 : 0, + (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0, + (cb_flag & IDWALK_INDIRECT_USAGE) ? 1 : 0); #endif - if (cb_flag & IDWALK_INDIRECT_USAGE) { - iter->count_indirect++; - } - else { - iter->count_direct++; + if (cb_flag & IDWALK_INDIRECT_USAGE) { + iter->count_indirect++; + } + else { + iter->count_direct++; + } } } @@ -1167,3 +1169,71 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo *is_used_local = (iter.count_direct != 0); *is_used_linked = (iter.count_indirect != 0); } + + +static int foreach_libblock_tag_unused_linked_data_callback(void *user_data, ID *self_id, ID **id_p, int UNUSED(cb_flag)) +{ + bool *is_changed = user_data; + + if (*id_p) { + /* XXX This is actually some kind of hack... + * Issue is, shapekeys' 'from' ID pointer is not actually ID usage. + * Maybe we should even nuke it from BKE_library_foreach_ID_link, not 100% sure yet... + */ + if ((GS(self_id->name) == ID_KE) && (((Key *)self_id)->from == *id_p)) { + return IDWALK_RET_NOP; + } + /* XXX another hack, for similar reasons as above one. */ + if ((GS(self_id->name) == ID_OB) && (((Object *)self_id)->proxy_from == (Object *)*id_p)) { + return IDWALK_RET_NOP; + } + + /* If checked id is used by an assumed used ID, then it is also used and not part of any linked archipelago. */ + if (!(self_id->tag & LIB_TAG_DOIT) && ((*id_p)->tag & LIB_TAG_DOIT)) { + (*id_p)->tag &= ~LIB_TAG_DOIT; + *is_changed = true; + } + } + + return IDWALK_RET_NOP; +} + +/** + * Detect orphaned linked data blocks (i.e. linked data not used (directly or indirectly) in any way by any local data), + * including complex cases like 'linked archipelagoes', i.e. linked datablocks that use each other in loops, + * which prevents their deletion by 'basic' usage checks... + * + * \param do_init_tag if \a true, all linked data are checked, if \a false, only linked datablocks already tagged with + * LIB_TAG_DOIT are checked. + */ +void BKE_library_tag_unused_linked_data(Main *bmain, const bool do_init_tag) +{ + ListBase *lb_array[MAX_LIBARRAY]; + + if (do_init_tag) { + int i = set_listbasepointers(bmain, lb_array); + + while (i--) { + for (ID *id = lb_array[i]->first; id; id = id->next) { + if (id->lib && (id->tag & LIB_TAG_INDIRECT) != 0) { + id->tag |= LIB_TAG_DOIT; + } + else { + id->tag &= ~LIB_TAG_DOIT; + } + } + } + } + + bool do_loop = true; + while (do_loop) { + int i = set_listbasepointers(bmain, lb_array); + do_loop = false; + + while (i--) { + for (ID *id = lb_array[i]->first; id; id = id->next) { + BKE_library_foreach_ID_link(id, foreach_libblock_tag_unused_linked_data_callback, &do_loop, IDWALK_NOP); + } + } + } +} |