|
Blender
V2.59
|
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