Added a new notifyer, NC_SPACE_CHANGED, to signal an editor that
authorJoseph Eagar <joeedh@gmail.com>
Thu, 21 Jan 2010 03:08:57 +0000 (03:08 +0000)
committerJoseph Eagar <joeedh@gmail.com>
Thu, 21 Jan 2010 03:08:57 +0000 (03:08 +0000)
replaces another so it can do updates (e.g. dopesheet editor can
sync channel selection).

Also coded a simple optimization for allocating small objects,
based on mempools.  It's #ifdef'd out, you can enabled it by
defining OPTIMIZE_SMALL_BLOCKS (e.g. adding -DDOPTIMIZE_SMALL_BLOCKS to
your compiler flags).

We suffer from a great deal of performance loss from the system allocator
(vgroups, ghash, edgehash, the singly-linked list implementation in blenlib,
editmesh, and likely a great many areas I'm forgetting), and this is the
common solution for handling the many-small-objects problem.  It's not
really production-ready yet (it's long-term memory consequencers need to
be profiled first, and the implementation tweaked as necassary), but for
people on systems with slow system allocators it's worth trying.

Note that since this creates a guardedalloc<->blenlib link, the build systems
need to be updated accordingly (I've already done this for scons, though I'm
not sure if the player builds).

intern/guardedalloc/intern/mallocn.c
source/blender/blenkernel/intern/BME_Customdata.c
source/blender/blenkernel/intern/BME_mesh.c
source/blender/blenkernel/intern/customdata.c
source/blender/blenlib/BLI_mempool.h
source/blender/blenlib/intern/BLI_mempool.c
source/blender/editors/screen/area.c
source/blender/editors/space_action/space_action.c
source/blender/makesdna/intern/SConscript
source/blender/makesrna/intern/SConscript
source/blender/windowmanager/WM_types.h

index d1114af2437c09e7a5a27e41cdd7ade2574701de..15236a2bee458f2ee81a89122de11f45fd5752d3 100644 (file)
@@ -37,6 +37,8 @@
 #include <string.h>    /* memcpy */
 #include <stdarg.h>
 
+#include "../../../source/blender/blenlib/BLI_mempool.h"
+
 /* mmap exception */
 #if defined(WIN32)
 #include <sys/types.h>
@@ -138,6 +140,40 @@ static int malloc_debug_memset= 0;
 /* implementation                                                        */
 /* --------------------------------------------------------------------- */
 
+#ifdef OPTIMIZE_SMALL_BLOCKS
+
+/*allocator for small objects
+
+  basic strategy: most slowdowns from overuse of the system allocator tends
+  to be from relatively small allocations.
+*/
+
+/*this is a little bit bigger then it perhaps needs to be, to accomodate vgroup
+  allocations (which are one system alloc per vertex of a mesh, and is a major
+  source of performance loss)*/
+#define SMALL_BLOCK_LIMIT      1024
+#define POOL_CHUNK_SIZE                512 /*size in number of elements, not bytes*/
+
+static BLI_mempool *alloc_pools[SMALL_BLOCK_LIMIT+sizeof(MemHead)+sizeof(MemTail)] = {NULL,};
+
+static void *mempool_alloc_mem(int size) {
+       size += sizeof(MemHead)+sizeof(MemTail);
+
+       if (!alloc_pools[size]) {
+               alloc_pools[size] = BLI_mempool_create(size, 1, POOL_CHUNK_SIZE, 1);
+       }
+       
+       return BLI_mempool_alloc(alloc_pools[size]);
+}
+
+static void mempool_free(void *mem, int size) {
+       size += sizeof(MemHead)+sizeof(MemTail);
+
+       BLI_mempool_free(alloc_pools[size], mem);
+}
+
+#endif
+
 static void print_error(const char *str, ...)
 {
        char buf[1024];
@@ -254,7 +290,14 @@ void *MEM_mallocN(unsigned int len, const char *str)
 
        len = (len + 3 ) & ~3;  /* allocate in units of 4 */
        
+#ifdef OPTIMIZE_SMALL_BLOCKS
+       if (len < SMALL_BLOCK_LIMIT)
+               memh= mempool_alloc_mem(len);
+       else
+               memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
+#else
        memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
+#endif
 
        if(memh) {
                make_memhead_header(memh, len, str);
@@ -276,7 +319,16 @@ void *MEM_callocN(unsigned int len, const char *str)
 
        len = (len + 3 ) & ~3;  /* allocate in units of 4 */
 
+#ifdef OPTIMIZE_SMALL_BLOCKS
+       if (len < SMALL_BLOCK_LIMIT) {
+               memh= mempool_alloc_mem(len);
+               memset(memh, 0, len+sizeof(MemHead)+sizeof(MemTail));
+       } else {
+               memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
+       }
+#else
        memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
+#endif
 
        if(memh) {
                make_memhead_header(memh, len, str);
@@ -629,6 +681,11 @@ static void rem_memblock(MemHead *memh)
         if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
             printf("Couldn't unmap memory %s\n", memh->name);
     }
+#ifdef OPTIMIZE_SMALL_BLOCKS
+       else if (memh->len < SMALL_BLOCK_LIMIT) {
+               mempool_free(memh, memh->len);
+       }
+#endif
     else {
                if(malloc_debug_memset && memh->len)
                        memset(memh+1, 255, memh->len);
index ea149e03959141b03e26f56ae90b7305d3f64bb6..822a0d8a08068e0356b5adfe2680861e33ae691e 100644 (file)
@@ -84,7 +84,7 @@ void BME_CD_Create(BME_CustomData *data, BME_CustomDataInit *init, int initalloc
        if(data->totlayer){
                /*alloc memory*/
                data->layers = MEM_callocN(sizeof(BME_CustomDataLayer)*data->totlayer, "BMesh Custom Data Layers");
-               data->pool = BLI_mempool_create(data->totsize, initalloc, initalloc);
+               data->pool = BLI_mempool_create(data->totsize, initalloc, initalloc, 0);
                /*initialize layer data*/
                for(i=0; i < BME_CD_NUMTYPES; i++){
                        if(init->layout[i]){
index f635cfcfcd2961e5db6a966d6dd612d1a91fd048..3b769a11ccb03bc0226d30abd1eca8c4efea7539 100644 (file)
@@ -55,10 +55,10 @@ BME_Mesh *BME_make_mesh(int allocsize[4])
        /*allocate the structure*/
        BME_Mesh *bm = MEM_callocN(sizeof(BME_Mesh),"BMesh");
        /*allocate the memory pools for the mesh elements*/
-       bm->vpool = BLI_mempool_create(sizeof(BME_Vert), allocsize[0], allocsize[0]);
-       bm->epool = BLI_mempool_create(sizeof(BME_Edge), allocsize[1], allocsize[1]);
-       bm->lpool = BLI_mempool_create(sizeof(BME_Loop), allocsize[2], allocsize[2]);
-       bm->ppool = BLI_mempool_create(sizeof(BME_Poly), allocsize[3], allocsize[3]);
+       bm->vpool = BLI_mempool_create(sizeof(BME_Vert), allocsize[0], allocsize[0], 0);
+       bm->epool = BLI_mempool_create(sizeof(BME_Edge), allocsize[1], allocsize[1], 0);
+       bm->lpool = BLI_mempool_create(sizeof(BME_Loop), allocsize[2], allocsize[2], 0);
+       bm->ppool = BLI_mempool_create(sizeof(BME_Poly), allocsize[3], allocsize[3], 0);
        return bm;
 }
 /*     
index fb31ed3d6107494e4f71d0aa6a95d0b7dc79edaa..3a8068ead56c9cc35a375a5ee3f29140b883423c 100644 (file)
@@ -1922,7 +1922,7 @@ void CustomData_from_bmeshpoly(CustomData *fdata, CustomData *pdata, CustomData
 
 
 void CustomData_bmesh_init_pool(CustomData *data, int allocsize){
-       if(data->totlayer)data->pool = BLI_mempool_create(data->totsize, allocsize, allocsize);
+       if(data->totlayer)data->pool = BLI_mempool_create(data->totsize, allocsize, allocsize, 0);
 }
 
 void CustomData_bmesh_free_block(CustomData *data, void **block)
index 8b31459dd382e02df735f4fa25f97390dc0294e5..a2fd80de417596709bf08dccd994c78d9b1c8fd0 100644 (file)
@@ -34,7 +34,7 @@
 struct BLI_mempool;
 typedef struct BLI_mempool BLI_mempool;
 
-BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk);
+BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk, int use_sysmalloc);
 void *BLI_mempool_alloc(BLI_mempool *pool);
 void *BLI_mempool_calloc(BLI_mempool *pool);
 void BLI_mempool_free(BLI_mempool *pool, void *addr);
index 485ba7cbd080c0672d35bf27cf57f47cac4fc58a..373b4d5add91cf93aeca07e99667b64bb4171d32 100644 (file)
@@ -49,9 +49,11 @@ typedef struct BLI_mempool{
        struct ListBase chunks;
        int esize, csize, pchunk;               /*size of elements and chunks in bytes and number of elements per chunk*/
        struct BLI_freenode     *free;          /*free element list. Interleaved into chunk datas.*/
+       int totalloc, totused; /*total number of elements allocated in total, and currently in use*/
+       int use_sysmalloc;
 }BLI_mempool;
 
-BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk)
+BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk, int use_sysmalloc)
 {      BLI_mempool  *pool = NULL;
        BLI_freenode *lasttail = NULL, *curnode = NULL;
        int i,j, maxchunks;
@@ -61,19 +63,20 @@ BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk)
                esize = sizeof(void*);
        
        /*allocate the pool structure*/
-       pool = MEM_mallocN(sizeof(BLI_mempool),"memory pool");
+       pool = use_sysmalloc ? malloc(sizeof(BLI_mempool)) : MEM_mallocN(sizeof(BLI_mempool), "memory pool");
        pool->esize = esize;
+       pool->use_sysmalloc = use_sysmalloc;
        pool->pchunk = pchunk;  
        pool->csize = esize * pchunk;
        pool->chunks.first = pool->chunks.last = NULL;
        
-       maxchunks = tote / pchunk;
+       maxchunks = tote / pchunk + 1;
        
        /*allocate the actual chunks*/
        for(i=0; i < maxchunks; i++){
-               BLI_mempool_chunk *mpchunk = MEM_mallocN(sizeof(BLI_mempool_chunk), "BLI_Mempool Chunk");
+               BLI_mempool_chunk *mpchunk = use_sysmalloc ? malloc(sizeof(BLI_mempool_chunk)) : MEM_mallocN(sizeof(BLI_mempool_chunk), "BLI_Mempool Chunk");
                mpchunk->next = mpchunk->prev = NULL;
-               mpchunk->data = MEM_mallocN(pool->csize, "BLI Mempool Chunk Data");
+               mpchunk->data = use_sysmalloc ? malloc(pool->csize) : MEM_mallocN(pool->csize, "BLI Mempool Chunk Data");
                BLI_addtail(&(pool->chunks), mpchunk);
                
                if(i==0) pool->free = mpchunk->data; /*start of the list*/
@@ -87,6 +90,8 @@ BLI_mempool *BLI_mempool_create(int esize, int tote, int pchunk)
                if(lasttail) lasttail->next = mpchunk->data;
                /*set the end of this chunks memoryy to the new tail for next iteration*/
                lasttail = curnode;
+
+               pool->totalloc += pool->pchunk;
        }
        /*terminate the list*/
        curnode->next = NULL;
@@ -98,11 +103,13 @@ void *BLI_mempool_alloc(BLI_mempool *pool){
        char *addr=NULL;
        int j;
 
+       pool->totused++;
+
        if(!(pool->free)){
                /*need to allocate a new chunk*/
-               BLI_mempool_chunk *mpchunk = MEM_mallocN(sizeof(BLI_mempool_chunk), "BLI_Mempool Chunk");
+               BLI_mempool_chunk *mpchunk = pool->use_sysmalloc ? malloc(sizeof(BLI_mempool_chunk)) :  MEM_mallocN(sizeof(BLI_mempool_chunk), "BLI_Mempool Chunk");
                mpchunk->next = mpchunk->prev = NULL;
-               mpchunk->data = MEM_mallocN(pool->csize, "BLI_Mempool Chunk Data");
+               mpchunk->data = pool->use_sysmalloc ? malloc(pool->csize) : MEM_mallocN(pool->csize, "BLI_Mempool Chunk Data");
                BLI_addtail(&(pool->chunks), mpchunk);
 
                pool->free = mpchunk->data; /*start of the list*/
@@ -112,6 +119,8 @@ void *BLI_mempool_alloc(BLI_mempool *pool){
                        curnode->next = (BLI_freenode*)addr;
                }
                curnode->next = NULL; /*terminate the list*/
+
+               pool->totalloc += pool->pchunk;
        }
 
        retval = pool->free;
@@ -128,16 +137,48 @@ void *BLI_mempool_calloc(BLI_mempool *pool){
 }
 
 
-
 void BLI_mempool_free(BLI_mempool *pool, void *addr){ //doesnt protect against double frees, dont be stupid!
        BLI_freenode *newhead = addr;
+       BLI_freenode *curnode=NULL;
+       char *tmpaddr=NULL;
+       int i;
+
        newhead->next = pool->free;
        pool->free = newhead;
+
+       pool->totused--;
+
+       /*nothing is in use; free all the chunks except the first*/
+       if (pool->totused == 0) {
+               BLI_mempool_chunk *mpchunk=NULL, *first;
+
+               first = pool->chunks.first;
+               BLI_remlink(&pool->chunks, first);
+
+               for(mpchunk = pool->chunks.first; mpchunk; mpchunk = mpchunk->next) 
+                       pool->use_sysmalloc ? free(mpchunk->data) : MEM_freeN(mpchunk->data);
+               
+               pool->use_sysmalloc ? BLI_freelist(&(pool->chunks)) : BLI_freelistN(&(pool->chunks));
+               
+               BLI_addtail(&pool->chunks, first);
+               pool->totalloc = pool->pchunk;
+
+               pool->free = first->data; /*start of the list*/
+               for(tmpaddr = first->data, i=0; i < pool->pchunk; i++){
+                       curnode = ((BLI_freenode*)tmpaddr);
+                       tmpaddr += pool->esize;
+                       curnode->next = (BLI_freenode*)tmpaddr;
+               }
+               curnode->next = NULL; /*terminate the list*/
+       }
 }
+
 void BLI_mempool_destroy(BLI_mempool *pool)
 {
        BLI_mempool_chunk *mpchunk=NULL;
-       for(mpchunk = pool->chunks.first; mpchunk; mpchunk = mpchunk->next) MEM_freeN(mpchunk->data);
-       BLI_freelistN(&(pool->chunks));
-       MEM_freeN(pool);
+       for(mpchunk = pool->chunks.first; mpchunk; mpchunk = mpchunk->next)
+               pool->use_sysmalloc ? free(mpchunk->data) : MEM_freeN(mpchunk->data);
+       
+       pool->use_sysmalloc ? BLI_freelist(&(pool->chunks)) : BLI_freelistN(&(pool->chunks));   
+       pool->use_sysmalloc ? free(pool) : MEM_freeN(pool);
 }
index 2ee786a0fb8231de105343e3743c984aa603770f..7fb4dd5767204e1a62b09cdb58715762084506e6 100644 (file)
@@ -1085,7 +1085,10 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type)
                
                /* tell WM to refresh, cursor types etc */
                WM_event_add_mousemove(C);
-               
+                               
+               /*send space change notifyer*/
+               WM_event_add_notifier(C, NC_SPACE|ND_SPACE_CHANGED, sa);
+
                ED_area_tag_redraw(sa);
                ED_area_tag_refresh(sa);
        }
@@ -1107,6 +1110,9 @@ void ED_area_prevspace(bContext *C, ScrArea *sa)
                ED_area_newspace(C, sa, SPACE_INFO);
        }
        ED_area_tag_redraw(sa);
+
+       /*send space change notifyer*/
+       WM_event_add_notifier(C, NC_SPACE|ND_SPACE_CHANGED, sa);
 }
 
 static char *editortype_pup(void)
@@ -1152,6 +1158,9 @@ static void spacefunc(struct bContext *C, void *arg1, void *arg2)
 {
        ED_area_newspace(C, CTX_wm_area(C), CTX_wm_area(C)->butspacetype);
        ED_area_tag_redraw(CTX_wm_area(C));
+
+       /*send space change notifyer*/
+       WM_event_add_notifier(C, NC_SPACE|ND_SPACE_CHANGED, CTX_wm_area(C));
 }
 
 /* returns offset for next button in header */
index 66e6e770ca4bfb85fe38db1aa01092724285e1a7..05801d5efbe7f6d70701ce0608184528f5c86da2 100644 (file)
@@ -137,7 +137,8 @@ static void action_free(SpaceLink *sl)
 /* spacetype; init callback */
 static void action_init(struct wmWindowManager *wm, ScrArea *sa)
 {
-
+       SpaceAction *saction = sa->spacedata.first;
+       saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
 }
 
 static SpaceLink *action_duplicate(SpaceLink *sl)
@@ -392,8 +393,15 @@ static void action_listener(ScrArea *sa, wmNotifier *wmn)
                        }
                        break;
                case NC_SPACE:
-                       if(wmn->data == ND_SPACE_DOPESHEET)
-                               ED_area_tag_redraw(sa);
+                       switch (wmn->data) {
+                               case ND_SPACE_DOPESHEET:
+                                       ED_area_tag_redraw(sa);
+                                       break;
+                               case ND_SPACE_CHANGED:
+                                       saction->flag |= SACTION_TEMP_NEEDCHANSYNC;
+                                       ED_area_tag_refresh(sa);
+                                       break;
+                       }                       
                        break;
        }
 }
index 120398791a88cc732f54f1c448f1c603e80aeaa7..8a0e738080cf5fb8c94311da6dc87168f2ec0b76 100644 (file)
@@ -50,7 +50,7 @@ targetdir = normpath(root_build_dir + '/makesdna')
 if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
        targetdir = '#' + targetdir
 
-makesdna = makesdna_tool.Program (target = targetdir, source = source_files, LIBS=['bf_guardedalloc'])
+makesdna = makesdna_tool.Program (target = targetdir, source = source_files, LIBS=['bf_guardedalloc', 'bf_blenlib'])
 
 dna_dict = dna.Dictionary()
 dna.Depends ('dna.c', makesdna)
index e5a08c9aeeaf3c6c92624bca5034dd59c5fa6a44..9491c4cf30e972dd7e4420762eb91c5e7fc1dfd3 100644 (file)
@@ -126,9 +126,9 @@ if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
        targetpath = '#' + targetpath
 
 if env['OURPLATFORM'] == 'linux2' and root_build_dir[0]==os.sep:
-       makesrna = makesrna_tool.Program (target = targetpath, source = source_files, LIBS=['bf_guardedalloc', 'bf_dna'])
+       makesrna = makesrna_tool.Program (target = targetpath, source = source_files, LIBS=['bf_guardedalloc', 'bf_dna', 'bf_blenlib'])
 else:
-       makesrna = makesrna_tool.Program (target = targetpath, source = source_files, LIBS=['bf_guardedalloc', 'bf_dna'])
+       makesrna = makesrna_tool.Program (target = targetpath, source = source_files, LIBS=['bf_guardedalloc', 'bf_dna', 'bf_blenlib'])
 
 rna_dict = rna.Dictionary()
 rna.Depends (generated_files, makesrna)
index eb8139ad62ecec921167fa9b694e0da63479266f..4caa4b973f14f9c72638f4044f03ce467bbd7929 100644 (file)
@@ -157,6 +157,7 @@ typedef struct wmNotifier {
 #define ND_SCREENCAST          (3<<16)
 #define ND_ANIMPLAY                    (4<<16)
 #define ND_GPENCIL                     (5<<16)
+#define ND_EDITOR_CHANGED      (6<<16) /*sent to new editors after switching to them*/
 
        /* NC_SCENE Scene */
 #define ND_SCENEBROWSE         (1<<16)
@@ -241,6 +242,7 @@ typedef struct wmNotifier {
 #define ND_SPACE_NLA                   (14<<16)
 #define ND_SPACE_SEQUENCER             (15<<16)
 #define ND_SPACE_NODE_VIEW             (16<<16)
+#define ND_SPACE_CHANGED               (17<<16) /*sent to a new editor type after it's replaced an old one*/
 
 /* subtype, 256 entries too */
 #define NOTE_SUBTYPE           0x0000FF00