Blender  V2.59
cache.c
Go to the documentation of this file.
00001 /*
00002  * $Id: cache.c 35239 2011-02-27 20:23:21Z jesterking $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * ***** END GPL LICENSE BLOCK *****
00021  */
00022 
00028 #include "MEM_guardedalloc.h"
00029 
00030 #include "BLI_utildefines.h"
00031 #include "BLI_ghash.h"
00032 #include "BLI_listbase.h"
00033 #include "BLI_memarena.h"
00034 #include "BLI_threads.h"
00035 
00036 
00037 
00038 #include "IMB_imbuf.h"
00039 #include "IMB_imbuf_types.h"
00040 #include "IMB_filetype.h"
00041 
00042 #include "imbuf.h"
00043 
00044 /* We use a two level cache here. A per-thread cache with limited number of
00045    tiles. This can be accessed without locking and so is hoped to lead to most
00046    tile access being lock-free. The global cache is shared between all threads
00047    and requires slow locking to access, and contains all tiles.
00048    
00049    The per-thread cache should be big enough that one might hope to not fall
00050    back to the global cache every pixel, but not to big to keep too many tiles
00051    locked and using memory. */
00052 
00053 #define IB_THREAD_CACHE_SIZE    100
00054 
00055 typedef struct ImGlobalTile {
00056         struct ImGlobalTile *next, *prev;
00057 
00058         ImBuf *ibuf;
00059         int tx, ty;
00060         int refcount;
00061         volatile int loading;
00062 } ImGlobalTile;
00063 
00064 typedef struct ImThreadTile {
00065         struct ImThreadTile *next, *prev;
00066 
00067         ImBuf *ibuf;
00068         int tx, ty;
00069 
00070         ImGlobalTile *global;
00071 } ImThreadTile;
00072 
00073 typedef struct ImThreadTileCache {
00074         ListBase tiles;
00075         ListBase unused;
00076         GHash *tilehash;
00077 } ImThreadTileCache;
00078 
00079 typedef struct ImGlobalTileCache {
00080         ListBase tiles;
00081         ListBase unused;
00082         GHash *tilehash;
00083 
00084         MemArena *memarena;
00085         uintptr_t totmem, maxmem;
00086 
00087         ImThreadTileCache thread_cache[BLENDER_MAX_THREADS+1];
00088         int totthread;
00089 
00090         ThreadMutex mutex;
00091 
00092         int initialized;
00093 } ImGlobalTileCache;
00094 
00095 static ImGlobalTileCache GLOBAL_CACHE;
00096 
00097 /***************************** Hash Functions ********************************/
00098 
00099 static unsigned int imb_global_tile_hash(const void *gtile_p)
00100 {
00101         const ImGlobalTile *gtile= gtile_p;
00102 
00103         return ((unsigned int)(intptr_t)gtile->ibuf)*769 + gtile->tx*53 + gtile->ty*97;
00104 }
00105 
00106 static int imb_global_tile_cmp(const void *a_p, const void *b_p)
00107 {
00108         const ImGlobalTile *a= a_p;
00109         const ImGlobalTile *b= b_p;
00110 
00111         if(a->ibuf == b->ibuf && a->tx == b->tx && a->ty == b->ty) return 0;
00112         else if(a->ibuf < b->ibuf || a->tx < b->tx || a->ty < b->ty) return -1;
00113         else return 1;
00114 }
00115 
00116 static unsigned int imb_thread_tile_hash(const void *ttile_p)
00117 {
00118         const ImThreadTile *ttile= ttile_p;
00119 
00120         return ((unsigned int)(intptr_t)ttile->ibuf)*769 + ttile->tx*53 + ttile->ty*97;
00121 }
00122 
00123 static int imb_thread_tile_cmp(const void *a_p, const void *b_p)
00124 {
00125         const ImThreadTile *a= a_p;
00126         const ImThreadTile *b= b_p;
00127 
00128         if(a->ibuf == b->ibuf && a->tx == b->tx && a->ty == b->ty) return 0;
00129         else if(a->ibuf < b->ibuf || a->tx < b->tx || a->ty < b->ty) return -1;
00130         else return 1;
00131 }
00132 
00133 /******************************** Load/Unload ********************************/
00134 
00135 static void imb_global_cache_tile_load(ImGlobalTile *gtile)
00136 {
00137         ImBuf *ibuf= gtile->ibuf;
00138         int toffs= ibuf->xtiles*gtile->ty + gtile->tx;
00139         unsigned int *rect;
00140 
00141         rect = MEM_callocN(sizeof(unsigned int)*ibuf->tilex*ibuf->tiley, "imb_tile");
00142         imb_loadtile(ibuf, gtile->tx, gtile->ty, rect);
00143         ibuf->tiles[toffs]= rect;
00144 }
00145 
00146 static void imb_global_cache_tile_unload(ImGlobalTile *gtile)
00147 {
00148         ImBuf *ibuf= gtile->ibuf;
00149         int toffs= ibuf->xtiles*gtile->ty + gtile->tx;
00150 
00151         MEM_freeN(ibuf->tiles[toffs]);
00152         ibuf->tiles[toffs]= NULL;
00153 
00154         GLOBAL_CACHE.totmem -= sizeof(unsigned int)*ibuf->tilex*ibuf->tiley;
00155 }
00156 
00157 /* external free */
00158 void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty)
00159 {
00160         ImGlobalTile *gtile, lookuptile;
00161 
00162         BLI_mutex_lock(&GLOBAL_CACHE.mutex);
00163 
00164         lookuptile.ibuf = ibuf;
00165         lookuptile.tx = tx;
00166         lookuptile.ty = ty;
00167         gtile= BLI_ghash_lookup(GLOBAL_CACHE.tilehash, &lookuptile);
00168 
00169         if(gtile) {
00170                 /* in case another thread is loading this */
00171                 while(gtile->loading)
00172                         ;
00173 
00174                 BLI_ghash_remove(GLOBAL_CACHE.tilehash, gtile, NULL, NULL);
00175                 BLI_remlink(&GLOBAL_CACHE.tiles, gtile);
00176                 BLI_addtail(&GLOBAL_CACHE.unused, gtile);
00177         }
00178 
00179         BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
00180 }
00181 
00182 /******************************* Init/Exit ***********************************/
00183 
00184 static void imb_thread_cache_init(ImThreadTileCache *cache)
00185 {
00186         ImThreadTile *ttile;
00187         int a;
00188 
00189         memset(cache, 0, sizeof(ImThreadTileCache));
00190 
00191         cache->tilehash= BLI_ghash_new(imb_thread_tile_hash, imb_thread_tile_cmp, "imb_thread_cache_init gh");
00192 
00193         /* pre-allocate all thread local tiles in unused list */
00194         for(a=0; a<IB_THREAD_CACHE_SIZE; a++) {
00195                 ttile= BLI_memarena_alloc(GLOBAL_CACHE.memarena, sizeof(ImThreadTile));
00196                 BLI_addtail(&cache->unused, ttile);
00197         }
00198 }
00199 
00200 static void imb_thread_cache_exit(ImThreadTileCache *cache)
00201 {
00202         BLI_ghash_free(cache->tilehash, NULL, NULL);
00203 }
00204 
00205 void imb_tile_cache_init(void)
00206 {
00207         memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
00208 
00209         BLI_mutex_init(&GLOBAL_CACHE.mutex);
00210 
00211         /* initialize for one thread, for places that access textures
00212            outside of rendering (displace modifier, painting, ..) */
00213         IMB_tile_cache_params(0, 0);
00214 
00215         GLOBAL_CACHE.initialized = 1;
00216 }
00217 
00218 void imb_tile_cache_exit(void)
00219 {
00220         ImGlobalTile *gtile;
00221         int a;
00222 
00223         if(GLOBAL_CACHE.initialized) {
00224                 for(gtile=GLOBAL_CACHE.tiles.first; gtile; gtile=gtile->next)
00225                         imb_global_cache_tile_unload(gtile);
00226 
00227                 for(a=0; a<GLOBAL_CACHE.totthread; a++)
00228                         imb_thread_cache_exit(&GLOBAL_CACHE.thread_cache[a]);
00229 
00230                 if(GLOBAL_CACHE.memarena)
00231                         BLI_memarena_free(GLOBAL_CACHE.memarena);
00232 
00233                 if(GLOBAL_CACHE.tilehash)
00234                         BLI_ghash_free(GLOBAL_CACHE.tilehash, NULL, NULL);
00235 
00236                 BLI_mutex_end(&GLOBAL_CACHE.mutex);
00237 
00238                 memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
00239         }
00240 }
00241 
00242 /* presumed to be called when no threads are running */
00243 void IMB_tile_cache_params(int totthread, int maxmem)
00244 {
00245         int a;
00246 
00247         /* always one cache for non-threaded access */
00248         totthread++;
00249 
00250         /* lazy initialize cache */
00251         if(GLOBAL_CACHE.totthread == totthread && GLOBAL_CACHE.maxmem == maxmem)
00252                 return;
00253 
00254         imb_tile_cache_exit();
00255 
00256         memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
00257 
00258         GLOBAL_CACHE.tilehash= BLI_ghash_new(imb_global_tile_hash, imb_global_tile_cmp, "tile_cache_params gh");
00259 
00260         GLOBAL_CACHE.memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "ImTileCache arena");
00261         BLI_memarena_use_calloc(GLOBAL_CACHE.memarena);
00262 
00263         GLOBAL_CACHE.maxmem= maxmem*1024*1024;
00264 
00265         GLOBAL_CACHE.totthread= totthread;
00266         for(a=0; a<totthread; a++)
00267                 imb_thread_cache_init(&GLOBAL_CACHE.thread_cache[a]);
00268 
00269         BLI_mutex_init(&GLOBAL_CACHE.mutex);
00270 }
00271 
00272 /***************************** Global Cache **********************************/
00273 
00274 static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, int tx, int ty, ImGlobalTile *replacetile)
00275 {
00276         ImGlobalTile *gtile, lookuptile;
00277 
00278         BLI_mutex_lock(&GLOBAL_CACHE.mutex);
00279 
00280         if(replacetile)
00281                 replacetile->refcount--;
00282 
00283         /* find tile in global cache */
00284         lookuptile.ibuf = ibuf;
00285         lookuptile.tx = tx;
00286         lookuptile.ty = ty;
00287         gtile= BLI_ghash_lookup(GLOBAL_CACHE.tilehash, &lookuptile);
00288         
00289         if(gtile) {
00290                 /* found tile. however it may be in the process of being loaded
00291                    by another thread, in that case we do stupid busy loop waiting
00292                    for the other thread to load the tile */
00293                 gtile->refcount++;
00294 
00295                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
00296 
00297                 while(gtile->loading)
00298                         ;
00299         }
00300         else {
00301                 /* not found, let's load it from disk */
00302 
00303                 /* first check if we hit the memory limit */
00304                 if(GLOBAL_CACHE.maxmem && GLOBAL_CACHE.totmem > GLOBAL_CACHE.maxmem) {
00305                         /* find an existing tile to unload */
00306                         for(gtile=GLOBAL_CACHE.tiles.last; gtile; gtile=gtile->prev)
00307                                 if(gtile->refcount == 0 && gtile->loading == 0)
00308                                         break;
00309                 }
00310 
00311                 if(gtile) {
00312                         /* found a tile to unload */
00313                         imb_global_cache_tile_unload(gtile);
00314                         BLI_ghash_remove(GLOBAL_CACHE.tilehash, gtile, NULL, NULL);
00315                         BLI_remlink(&GLOBAL_CACHE.tiles, gtile);
00316                 }
00317                 else {
00318                         /* allocate a new tile or reuse unused */
00319                         if(GLOBAL_CACHE.unused.first) {
00320                                 gtile= GLOBAL_CACHE.unused.first;
00321                                 BLI_remlink(&GLOBAL_CACHE.unused, gtile);
00322                         }
00323                         else
00324                                 gtile= BLI_memarena_alloc(GLOBAL_CACHE.memarena, sizeof(ImGlobalTile));
00325                 }
00326 
00327                 /* setup new tile */
00328                 gtile->ibuf= ibuf;
00329                 gtile->tx= tx;
00330                 gtile->ty= ty;
00331                 gtile->refcount= 1;
00332                 gtile->loading= 1;
00333 
00334                 BLI_ghash_insert(GLOBAL_CACHE.tilehash, gtile, gtile);
00335                 BLI_addhead(&GLOBAL_CACHE.tiles, gtile);
00336 
00337                 /* mark as being loaded and unlock to allow other threads to load too */
00338                 GLOBAL_CACHE.totmem += sizeof(unsigned int)*ibuf->tilex*ibuf->tiley;
00339 
00340                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
00341 
00342                 /* load from disk */
00343                 imb_global_cache_tile_load(gtile);
00344 
00345                 /* mark as done loading */
00346                 gtile->loading= 0;
00347         }
00348 
00349         return gtile;
00350 }
00351 
00352 /***************************** Per-Thread Cache ******************************/
00353 
00354 static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache, ImBuf *ibuf, int tx, int ty)
00355 {
00356         ImThreadTile *ttile, lookuptile;
00357         ImGlobalTile *gtile, *replacetile;
00358         int toffs= ibuf->xtiles*ty + tx;
00359 
00360         /* test if it is already in our thread local cache */
00361         if((ttile=cache->tiles.first)) {
00362                 /* check last used tile before going to hash */
00363                 if(ttile->ibuf == ibuf && ttile->tx == tx && ttile->ty == ty)
00364                         return ibuf->tiles[toffs];
00365 
00366                 /* find tile in hash */
00367                 lookuptile.ibuf = ibuf;
00368                 lookuptile.tx = tx;
00369                 lookuptile.ty = ty;
00370 
00371                 if((ttile=BLI_ghash_lookup(cache->tilehash, &lookuptile))) {
00372                         BLI_remlink(&cache->tiles, ttile);
00373                         BLI_addhead(&cache->tiles, ttile);
00374 
00375                         return ibuf->tiles[toffs];
00376                 }
00377         }
00378 
00379         /* not found, have to do slow lookup in global cache */
00380         if(cache->unused.first == NULL) {
00381                 ttile= cache->tiles.last;
00382                 replacetile= ttile->global;
00383                 BLI_remlink(&cache->tiles, ttile);
00384                 BLI_ghash_remove(cache->tilehash, ttile, NULL, NULL);
00385         }
00386         else {
00387                 ttile= cache->unused.first;
00388                 replacetile= NULL;
00389                 BLI_remlink(&cache->unused, ttile);
00390         }
00391 
00392         BLI_addhead(&cache->tiles, ttile);
00393         BLI_ghash_insert(cache->tilehash, ttile, ttile);
00394 
00395         gtile= imb_global_cache_get_tile(ibuf, tx, ty, replacetile);
00396 
00397         ttile->ibuf= gtile->ibuf;
00398         ttile->tx= gtile->tx;
00399         ttile->ty= gtile->ty;
00400         ttile->global= gtile;
00401 
00402         return ibuf->tiles[toffs];
00403 }
00404 
00405 unsigned int *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread)
00406 {
00407         return imb_thread_cache_get_tile(&GLOBAL_CACHE.thread_cache[thread+1], ibuf, tx, ty);
00408 }
00409 
00410 void IMB_tiles_to_rect(ImBuf *ibuf)
00411 {
00412         ImBuf *mipbuf;
00413         ImGlobalTile *gtile;
00414         unsigned int *to, *from;
00415         int a, tx, ty, y, w, h;
00416 
00417         for(a=0; a<ibuf->miptot; a++) {
00418                 mipbuf= IMB_getmipmap(ibuf, a);
00419 
00420                 /* don't call imb_addrectImBuf, it frees all mipmaps */
00421                 if(!mipbuf->rect) {
00422                         if((mipbuf->rect = MEM_mapallocN(ibuf->x*ibuf->y*sizeof(unsigned int), "imb_addrectImBuf"))) {
00423                                 mipbuf->mall |= IB_rect;
00424                                 mipbuf->flags |= IB_rect;
00425                         }
00426                         else
00427                                 break;
00428                 }
00429 
00430                 for(ty=0; ty<mipbuf->ytiles; ty++) {
00431                         for(tx=0; tx<mipbuf->xtiles; tx++) {
00432                                 /* acquire tile through cache, this assumes cache is initialized,
00433                                    which it is always now but it's a weak assumption ... */
00434                                 gtile= imb_global_cache_get_tile(mipbuf, tx, ty, NULL);
00435 
00436                                 /* setup pointers */
00437                                 from= mipbuf->tiles[mipbuf->xtiles*ty + tx];
00438                                 to= mipbuf->rect + mipbuf->x*ty*mipbuf->tiley + tx*mipbuf->tilex;
00439 
00440                                 /* exception in tile width/height for tiles at end of image */
00441                                 w= (tx == mipbuf->xtiles-1)? mipbuf->x - tx*mipbuf->tilex: mipbuf->tilex;
00442                                 h= (ty == mipbuf->ytiles-1)? mipbuf->y - ty*mipbuf->tiley: mipbuf->tiley;
00443 
00444                                 for(y=0; y<h; y++) {
00445                                         memcpy(to, from, sizeof(unsigned int)*w);
00446                                         from += mipbuf->tilex;
00447                                         to += mipbuf->x;
00448                                 }
00449 
00450                                 /* decrease refcount for tile again */
00451                                 BLI_mutex_lock(&GLOBAL_CACHE.mutex);
00452                                 gtile->refcount--;
00453                                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
00454                         }
00455                 }
00456         }
00457 }
00458