Index: source/blender/blenlib/BLI_ghash.h =================================================================== --- source/blender/blenlib/BLI_ghash.h (revision 47115) +++ source/blender/blenlib/BLI_ghash.h (working copy) @@ -70,6 +70,7 @@ void BLI_ghash_insert(GHash *gh, void *key, void *val); void *BLI_ghash_lookup(GHash *gh, const void *key); int BLI_ghash_remove(GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp); +void *BLI_ghash_pop(GHash *gh, void *key, GHashKeyFreeFP keyfreefp); int BLI_ghash_haskey(GHash *gh, void *key); int BLI_ghash_size(GHash *gh); Index: source/blender/blenlib/intern/BLI_ghash.c =================================================================== --- source/blender/blenlib/intern/BLI_ghash.c (revision 47115) +++ source/blender/blenlib/intern/BLI_ghash.c (working copy) @@ -132,18 +132,14 @@ if (gh->cmpfp(key, e->key) == 0) { Entry *n = e->next; - if (keyfreefp) - keyfreefp(e->key); - if (valfreefp) - valfreefp(e->val); + if (keyfreefp) keyfreefp(e->key); + if (valfreefp) valfreefp(e->val); BLI_mempool_free(gh->entrypool, e); /* correct but 'e' isn't used before return */ /* e= n; *//*UNUSED*/ - if (p) - p->next = n; - else - gh->buckets[hash] = n; + if (p) p->next = n; + else gh->buckets[hash] = n; gh->nentries--; return 1; @@ -154,6 +150,35 @@ return 0; } +/* same as above but return the value */ +void *BLI_ghash_pop(GHash *gh, void *key, GHashKeyFreeFP keyfreefp) +{ + unsigned int hash = gh->hashfp(key) % gh->nbuckets; + Entry *e; + Entry *p = NULL; + + for (e = gh->buckets[hash]; e; e = e->next) { + if (gh->cmpfp(key, e->key) == 0) { + Entry *n = e->next; + void *value = e->val; + + if (keyfreefp) keyfreefp(e->key); + BLI_mempool_free(gh->entrypool, e); + + /* correct but 'e' isn't used before return */ + /* e= n; *//*UNUSED*/ + if (p) p->next = n; + else gh->buckets[hash] = n; + + gh->nentries--; + return value; + } + p = e; + } + + return NULL; +} + int BLI_ghash_haskey(GHash *gh, void *key) { unsigned int hash = gh->hashfp(key) % gh->nbuckets; Index: source/blender/editors/space_outliner/outliner_tree.c =================================================================== --- source/blender/editors/space_outliner/outliner_tree.c (revision 47115) +++ source/blender/editors/space_outliner/outliner_tree.c (working copy) @@ -63,6 +63,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLI_ghash.h" +#include "BLI_mempool.h" #include "BKE_fcurve.h" #include "BKE_main.h" @@ -78,6 +80,7 @@ #include "RNA_access.h" #include "outliner_intern.h" +#include "PIL_time.h" /* ********************************************************* */ /* Defines */ @@ -132,27 +135,93 @@ } } -/* XXX - THIS FUNCTION IS INCREDIBLY SLOW - * ... it can bring blenders tools and viewport to a grinding halt becuase of searching - * for duplicate items every times they are added. - * - * TODO (possible speedups) - * - use a hash for duplicate (could even store a hash per type) - * - use mempool for TreeElements - * */ +//#define USE_TSELEM_HASH_SPEEDUP +static int iii = 0; + +#ifdef USE_TSELEM_HASH_SPEEDUP +typedef struct OutlinerTreeHash { + GHash *tselem_type_hash[TSE_TOTAL_TYPES]; + BLI_mempool *tselem_key_pool; +} OutlinerTreeHash; + +typedef struct TreeStoreElem_Key { + TreeStoreElem tselem; + BLI_mempool *keys_pool; /* annoying - all need to point to the keypool or we would need globals */ +} TreeStoreElem_Key; + +static unsigned int outliner_tselem_hash(const void *tselem_p) +{ + const TreeStoreElem *tselem = tselem_p; + + return ((unsigned int)(intptr_t)tselem->id) * 769 + + tselem->type * 53 + + ((tselem->type == 0 ? 0 : tselem->nr) * 97); +} + +static int outliner_tselem_cmp(const void *a_p, const void *b_p) +{ + const TreeStoreElem *a = a_p; + const TreeStoreElem *b = b_p; + + if (a->type == b->type && a->id == b->id && (a->type == 0 || a->nr == b->nr)) return 0; + else if (a->type < b->type || a->id < b->id || (a->type != 0 && a->nr < b->nr)) return -1; + else return 1; +} + +static void outliner_tselem_keyfree(void *val) +{ + TreeStoreElem_Key *key = (TreeStoreElem_Key *)val; + + BLI_mempool_free(key->keys_pool, key); +} + +#endif + + + static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short type, short nr) { TreeStore *ts; TreeStoreElem *tselem; + +#ifdef USE_TSELEM_HASH_SPEEDUP + TreeStoreElem_Key *tselem_key; + BLI_mempool *tselem_key_pool; + GHash *gh = ((OutlinerTreeHash *)soops->tselem_hash)->tselem_type_hash[type]; + int tselem_index; + TreeStoreElem tselem_cmp = {0}; + tselem_cmp.id = id; + tselem_cmp.type = type; + tselem_cmp.nr = nr; +#else int a; - +#endif + /* case 1; no TreeStore */ if (soops->treestore == NULL) { soops->treestore = MEM_callocN(sizeof(TreeStore), "treestore"); } ts = soops->treestore; - + /* check if 'te' is in treestore */ +#ifdef USE_TSELEM_HASH_SPEEDUP + /* we could use a BLI_ghash_pop() function here */ + tselem_index = GET_INT_FROM_POINTER(BLI_ghash_pop(gh, &tselem_cmp, outliner_tselem_keyfree)); + + if (tselem_index) { + tselem_index--; + printf("%d\n", tselem_index); + tselem = &ts->data[tselem_index]; + te->store_index = tselem_index; + tselem->used = 1; + + return; + } +#else + /* XXX - THIS FUNCTION IS INCREDIBLY SLOW + * ... it can bring blenders tools and viewport to a grinding halt becuase of searching + * for duplicate items every times they are added. + */ tselem = ts->data; for (a = 0; a < ts->usedelem; a++, tselem++) { if ((tselem->used == 0) && (tselem->type == type) && (tselem->id == id)) { @@ -163,7 +232,8 @@ } } } - +#endif /* !USE_TSELEM_HASH_SPEEDUP */ + /* add 1 element to treestore */ if (ts->usedelem == ts->totelem) { TreeStoreElem *tsnew; @@ -187,6 +257,14 @@ tselem->flag = TSE_CLOSED; te->store_index = ts->usedelem; +#ifdef USE_TSELEM_HASH_SPEEDUP + tselem_key_pool = ((OutlinerTreeHash *)soops->tselem_hash)->tselem_key_pool; + tselem_key = BLI_mempool_alloc(tselem_key_pool); + tselem_key->tselem = *tselem; + tselem_key->keys_pool = tselem_key_pool; + BLI_ghash_insert(gh, tselem_key, SET_INT_IN_POINTER(ts->usedelem + 1)); +#endif + ts->usedelem++; } @@ -1197,7 +1275,7 @@ { const tTreeSort *x1 = v1, *x2 = v2; int comp; - + iii++; /* first put objects last (hierarchy) */ comp = (x1->idcode == ID_OB); if (x2->idcode == ID_OB) comp += 2; @@ -1395,6 +1473,10 @@ TreeStoreElem *tselem; int show_opened = (soops->treestore == NULL); /* on first view, we open scenes */ +#ifdef USE_TSELEM_HASH_SPEEDUP + OutlinerTreeHash tselem_cache = {{NULL}}; +#endif + /* Are we looking for something - we want to tag parents to filter child matches * - NOT in datablocks view - searching all datablocks takes way too long to be useful * - this variable is only set once per tree build */ @@ -1406,6 +1488,19 @@ if (soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW)) return; +#ifdef USE_TSELEM_HASH_SPEEDUP + soops->tselem_hash = (void *)&tselem_cache; + { + int i; + for (i = 0; i < TSE_TOTAL_TYPES; i++) { + tselem_cache.tselem_type_hash[i] = BLI_ghash_new(outliner_tselem_hash, outliner_tselem_cmp, __func__); + } + tselem_cache.tselem_key_pool = BLI_mempool_create(sizeof(TreeStoreElem_Key), 512, 512, 0); // BLI_MEMPOOL_SYSMALLOC + } +#endif + + TIMEIT_START(oops); + outliner_free_tree(&soops->tree); outliner_storage_cleanup(soops); @@ -1520,11 +1615,11 @@ int op; if (ed == NULL) - return; + goto cleanup; seq = ed->seqbasep->first; if (!seq) - return; + goto cleanup; while (seq) { op = need_add_seq_dup(seq); @@ -1575,8 +1670,34 @@ if (ten) ten->directdata = BASACT; } +cleanup: + (void)0; + +#ifdef USE_TSELEM_HASH_SPEEDUP + { + int i; + for (i = 0; i < TSE_TOTAL_TYPES; i++) { + BLI_ghash_free(tselem_cache.tselem_type_hash[i], NULL, NULL); + tselem_cache.tselem_type_hash[i] = NULL; + } + soops->tselem_hash = NULL; + BLI_mempool_destroy(tselem_cache.tselem_key_pool); + } +#endif + + TIMEIT_END(oops); + + { + TreeStore *ts = soops->treestore; + printf("iter %d\n", iii); + printf("ts->usedelem %d\n", ts->usedelem); + iii = 0; + } + outliner_sort(soops, &soops->tree); outliner_filter_tree(soops, &soops->tree); + + } Index: source/blender/editors/space_outliner/outliner_intern.h =================================================================== --- source/blender/editors/space_outliner/outliner_intern.h (revision 47115) +++ source/blender/editors/space_outliner/outliner_intern.h (working copy) @@ -103,6 +103,7 @@ #define TSE_NLA_TRACK 33 #define TSE_KEYMAP 34 #define TSE_KEYMAP_ITEM 35 +#define TSE_TOTAL_TYPES 36 /* keep updated! */ /* button events */ #define OL_NAMEBUTTON 1 Index: source/blender/makesdna/DNA_space_types.h =================================================================== --- source/blender/makesdna/DNA_space_types.h (revision 47115) +++ source/blender/makesdna/DNA_space_types.h (working copy) @@ -241,6 +241,8 @@ struct TreeStoreElem search_tse; short flag, outlinevis, storeflag, search_flags; + + void *tselem_hash; /* runtime */ } SpaceOops;