Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
pixelpipe_cache.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel
3 Copyright (C) 2025 - Aurélien PIERRE
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20#include "common/darktable.h"
21#include "common/debug.h"
22#include "develop/format.h"
24#include <inttypes.h>
25#include <glib.h>
26#include <stdlib.h>
27
28
30{
31 uint64_t hash; // unique identifier of the entry
32 void *data; // buffer holding pixels... or anything else
33 size_t size; // size of the data buffer
34 dt_iop_buffer_dsc_t dsc; // metadata of the data buffer
35 int64_t age; // timestamp of creation. Oldest entry will be the first freed if it's not locked
36 char *name; // name of the cache entry, for debugging
37 int id; // id of the pipeline owning this entry. Used when flushing, a pipe can only flush its own.
38 dt_atomic_int refcount; // reference count for the cache entry, to avoid freeing it while still in use
39 dt_pthread_rwlock_t lock; // read/write lock to avoid threads conflicts
40 gboolean auto_destroy; // TRUE for auto-destruction the next time it's used. Used for short-lived entries (transient states).
42
43
45 dt_pixel_cache_entry_t *cache_entry);
46
48{
49 return (dt_pixel_cache_entry_t *)g_hash_table_lookup(cache->entries, GINT_TO_POINTER(hash));
50}
51
52
60
61
63{
64 return cache_entry->size / (1024 * 1024);
65}
66
67
68void dt_pixel_cache_message(dt_pixel_cache_entry_t *cache_entry, const char *message, gboolean verbose)
69{
70 if(!(darktable.unmuted & DT_DEBUG_PIPE)) return;
71 if(verbose && !(darktable.unmuted & DT_DEBUG_VERBOSE)) return;
72 dt_print(DT_DEBUG_PIPE, "[pixelpipe] cache entry %" PRIu64 ": %s (%lu MiB - age %" PRId64 ") %s\n", cache_entry->hash,
73 cache_entry->name, dt_pixel_cache_get_size(cache_entry), cache_entry->age, message);
74}
75
76
77// remove the cache entry with the given hash and update the cache memory usage
78// WARNING: not internally thread-safe, protect its calls with mutex lock
79// return 0 on success, 1 on error
80int _non_thread_safe_cache_remove(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const gboolean force,
81 dt_pixel_cache_entry_t *cache_entry)
82{
83 if(cache_entry == NULL)
84 cache_entry = _non_threadsafe_cache_get_entry(cache, hash);
85
86 if(cache_entry)
87 {
88 // Returns 1 if the lock is captured by another thread
89 // 0 if WE capture the lock, and then need to release it
90 gboolean locked = dt_pthread_rwlock_trywrlock(&cache_entry->lock);
91 if(!locked) dt_pthread_rwlock_unlock(&cache_entry->lock);
92 gboolean used = dt_atomic_get_int(&cache_entry->refcount) > 0;
93
94 if((!used || force) && !locked)
95 {
96 cache->current_memory -= cache_entry->size;
97 g_hash_table_remove(cache->entries, GINT_TO_POINTER(hash));
98 return 0;
99 }
100 else if(used)
101 dt_pixel_cache_message(cache_entry, "cannot remove: used", TRUE);
102 else if(locked)
103 dt_pixel_cache_message(cache_entry, "cannot remove: locked", TRUE);
104 }
105 else
106 {
107 dt_print(DT_DEBUG_PIPE, "[pixelpipe] cache entry %" PRIu64 " not found, will not be removed\n", hash);
108 }
109 return 1;
110}
111
112
113int dt_dev_pixelpipe_cache_remove(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const gboolean force,
114 dt_pixel_cache_entry_t *cache_entry)
115{
117 int error = _non_thread_safe_cache_remove(cache, hash, force, cache_entry);
119 return error;
120}
121
128
129
130// find the cache entry hash with the oldest use
131void _cache_get_oldest(gpointer key, gpointer value, gpointer user_data)
132{
133 dt_pixel_cache_entry_t *cache_entry = (dt_pixel_cache_entry_t *)value;
134 _cache_lru_t *lru = (_cache_lru_t *)user_data;
135
136 // Don't remove LRU entries that are still in use
137 // NOTE: with all the killswitches mechanisms and safety measures,
138 // we might have more things decreasing refcount than increasing it.
139 // It's no big deal though, as long as the (final output) backbuf
140 // is checked for NULL and not reused if pipeline is DIRTY.
141 if(cache_entry->age < lru->max_age)
142 {
143 // Returns 1 if the lock is captured by another thread
144 // 0 if WE capture the lock, and then need to release it
145 gboolean locked = dt_pthread_rwlock_trywrlock(&cache_entry->lock);
146 if(!locked) dt_pthread_rwlock_unlock(&cache_entry->lock);
147 gboolean used = dt_atomic_get_int(&cache_entry->refcount) > 0;
148
149 if(!locked && !used)
150 {
151 lru->max_age = cache_entry->age;
152 lru->hash = cache_entry->hash;
153 lru->cache_entry = cache_entry;
154 dt_pixel_cache_message(cache_entry, "candidate for deletion", TRUE);
155 }
156 else if(used)
157 dt_pixel_cache_message(cache_entry, "cannot be deleted: used", TRUE);
158 else if(locked)
159 dt_pixel_cache_message(cache_entry, "cannot be deleted: locked", TRUE);
160 }
161}
162
163
164// remove the least used cache entry
165// return 0 on success, 1 on error
166// error is : we couldn't find a candidate for deletion because all entries are either locked or in use
167// or we found one but failed to remove it.
169{
170 _cache_lru_t *lru = (_cache_lru_t *)malloc(sizeof(_cache_lru_t));
171 lru->max_age = g_get_monotonic_time();
172 lru->hash = 0;
173 lru->cache_entry = NULL;
174 int error = 1;
175 g_hash_table_foreach(cache->entries, _cache_get_oldest, lru);
176
177 if(lru->hash > 0)
179 else
180 dt_print(DT_DEBUG_PIPE, "[pixelpipe] couldn't remove LRU, %i items and all are used\n", g_hash_table_size(cache->entries));
181
182 free(lru);
183 return error;
184}
185
186// return 0 on success 1 on error
194
195// WARNING: not thread-safe, protect its calls with mutex lock
197 const dt_iop_buffer_dsc_t dsc, const char *name, const int id,
199{
200 // Dynamically update the max cache size depending on remaining free memory on system
201 const size_t remaining_mem = dt_get_available_mem();
202 const size_t safety_margin = 4 * dt_get_singlebuffer_mem();
203 if(remaining_mem < safety_margin)
204 {
205 cache->max_memory
206 = MIN(MAX((int64_t)remaining_mem - (int64_t)safety_margin, (int64_t)2 * dt_get_singlebuffer_mem()),
207 remaining_mem);
208 //fprintf(stdout, "new pipeline cache size : %lu MiB\n", cache->max_memory / (1024 * 1024));
209 if(cache->max_memory == 2 * dt_get_singlebuffer_mem())
210 dt_control_log(_("Your system RAM is nearly saturated.\n"
211 "Processing full-resolution images may not "
212 "possible anymore.\n"));
213 }
214
215 // Free up space if needed to match the max memory limit
216 // If error, all entries are currently locked or in use, so we cannot free space to allocate a new entry.
217 int error = 0;
218 while(cache->current_memory + size > cache->max_memory && g_hash_table_size(cache->entries) > 0 && !error)
220
221 if(cache->current_memory + size > cache->max_memory)
222 {
223 dt_print(DT_DEBUG_PIPE, "[pixelpipe] cache is full, cannot allocate new entry %" PRIu64 " (%s)\n", hash, name);
224 return NULL; // not enough memory
225 }
226
228 if(!cache_entry) return NULL;
229
230 // allocate the data buffer
231 cache_entry->data = dt_alloc_align(size);
232
233 // if allocation failed, remove the least recently used cache entry, then try again
234 while(cache_entry->data == NULL && g_hash_table_size(cache->entries) > 0)
235 {
237 cache_entry->data = dt_alloc_align(size);
238 }
239
240 if(!cache_entry->data)
241 {
242 free(cache_entry);
243 return NULL;
244 }
245
246 cache_entry->size = size;
247 cache_entry->age = 0;
248 cache_entry->dsc = dsc;
249 cache_entry->hash = hash;
250 cache_entry->id = id;
251 cache_entry->name = g_strdup(name);
252 cache_entry->refcount = 0;
253 cache_entry->auto_destroy = FALSE;
254 dt_pthread_rwlock_init(&cache_entry->lock, NULL);
255
256 g_hash_table_insert(cache->entries, GINT_TO_POINTER(hash), cache_entry);
257 cache->current_memory += size;
258
259 return cache_entry;
260}
261
262
264{
265 if(!cache_entry) return;
266 dt_pixel_cache_message(cache_entry, "freed", FALSE);
267 dt_free_align(cache_entry->data);
268 cache_entry->data = NULL;
269 dt_pthread_rwlock_destroy(&cache_entry->lock);
270 g_free(cache_entry->name);
271 free(cache_entry);
272}
273
274
276{
278 dt_pthread_mutex_init(&cache->lock, NULL);
279 cache->entries = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)_free_cache_entry);
280 cache->max_memory = max_memory;
281 cache->current_memory = 0;
282 cache->queries = cache->hits = 0;
283 return cache;
284}
285
286
288{
289 if(!cache) return;
291 g_hash_table_destroy(cache->entries);
292 cache->entries = NULL;
293}
294
295
297 const size_t size, const char *name, const int id,
298 void **data, dt_iop_buffer_dsc_t **dsc,
300{
302
303 cache->queries++;
304
305 // Find the cache entry for this hash, if any
307 gboolean cache_entry_found = (cache_entry != NULL);
308
309 if(cache_entry)
310 cache->hits++;
311 else
312 cache_entry = dt_pixel_cache_new_entry(hash, size, **dsc, name, id, cache);
313
314 if(cache_entry)
315 {
316 if(cache_entry_found)
317 {
318 // Block and wait for write events to finish, aka try to take a read lock
319 dt_dev_pixelpipe_cache_rdlock_entry(cache, hash, TRUE, cache_entry);
320 dt_dev_pixelpipe_cache_rdlock_entry(cache, hash, FALSE, cache_entry);
321 }
322 else
323 {
324 // Newly-allocated buffer: immediately lock in write mode until the caller
325 // populates the content, so other threads may not lock it in read mode
326 // before there is actually something to read.
327 dt_dev_pixelpipe_cache_wrlock_entry(cache, hash, TRUE, cache_entry);
328 }
329
330 // Set the time after we get the lock
331 cache_entry->age = g_get_monotonic_time(); // this is the MRU entry
332 *data = cache_entry->data;
333 *dsc = &cache_entry->dsc;
334 dt_pixel_cache_message(cache_entry, (cache_entry_found) ? "found" : "created", FALSE);
335
336 // This will become the input for the next module, lock it until next module process ends
338 }
339 else
340 {
341 // Don't write on *dsc and *data here
342 dt_print(DT_DEBUG_PIPE, "couldn't allocate new cache entry %" PRIu64 "\n", hash);
343 }
344
345 if(entry) *entry = cache_entry;
346
348 return !cache_entry_found;
349}
350
352 void **data, dt_iop_buffer_dsc_t **dsc, dt_pixel_cache_entry_t **entry)
353{
354 // Find the cache entry for this hash, if any
356 cache->queries++;
358 if(cache_entry) cache->hits++;
359
360 if(cache_entry)
361 {
362 // Block and wait for write events to finish, aka try to take a read lock
363 dt_dev_pixelpipe_cache_rdlock_entry(cache, hash, TRUE, cache_entry);
364 dt_dev_pixelpipe_cache_rdlock_entry(cache, hash, FALSE, cache_entry);
365
366 // Set the time after we get the lock
367 cache_entry->age = g_get_monotonic_time(); // this is the MRU entry
368 *data = cache_entry->data;
369 *dsc = &cache_entry->dsc;
370 dt_pixel_cache_message(cache_entry, "found", FALSE);
371
372 // This will become the input for the next module, lock it until next module process ends
374 }
375
376 if(entry) *entry = cache_entry;
377
379 return cache_entry != NULL;
380}
381
382
383gboolean _for_each_remove(gpointer key, gpointer value, gpointer user_data)
384{
385 dt_pixel_cache_entry_t *cache_entry = (dt_pixel_cache_entry_t *)value;
386 const int id = GPOINTER_TO_INT(user_data);
387
388 // Returns 1 if the lock is captured by another thread
389 // 0 if WE capture the lock, and then need to release it
390 gboolean locked = dt_pthread_rwlock_trywrlock(&cache_entry->lock);
391 if(!locked) dt_pthread_rwlock_unlock(&cache_entry->lock);
392 gboolean used = dt_atomic_get_int(&cache_entry->refcount) > 0;
393
394 return (cache_entry->id == id || id == -1) && !used && !locked;
395}
396
398{
400 g_hash_table_foreach_remove(cache->entries, _for_each_remove, GINT_TO_POINTER(id));
401 cache->current_memory = 0;
403}
404
410
411
413{
414 GHashTableIter iter;
415 gpointer key, value;
416 uint64_t hash = 0;
417 if(entry) *entry = NULL;
418
419 g_hash_table_iter_init(&iter, cache->entries);
420 while(g_hash_table_iter_next(&iter, &key, &value))
421 {
422 dt_pixel_cache_entry_t *cache_entry = (dt_pixel_cache_entry_t *)value;
423 if(cache_entry->data == data)
424 {
425 hash = cache_entry->hash;
426 if(entry) *entry = cache_entry;
427 break;
428 }
429 }
430
431 return hash;
432}
433
434
442
443
445{
446 dt_pixel_cache_entry_t *cache_entry;
448
449 _non_thread_safe_cache_get_hash_data(cache, data, &cache_entry);
450 if(cache_entry)
451 {
452 dt_pthread_rwlock_rdlock(&cache_entry->lock);
453 dt_pixel_cache_message(cache_entry, "read lock", TRUE);
454 }
455
457 return cache_entry;
458}
459
460
462 dt_pixel_cache_entry_t *cache_entry)
463{
464 if(cache_entry == NULL)
465 cache_entry = _non_threadsafe_cache_get_entry(cache, hash);
466
467 if(cache_entry)
468 {
469 if(lock)
470 {
471 dt_atomic_add_int(&cache_entry->refcount, 1);
472 dt_pixel_cache_message(cache_entry, "ref count ++", TRUE);
473 }
474 else
475 {
476 dt_atomic_sub_int(&cache_entry->refcount, 1);
477 dt_pixel_cache_message(cache_entry, "ref count --", TRUE);
478 }
479 }
480}
481
482
484 dt_pixel_cache_entry_t *cache_entry)
485{
487 _non_thread_safe_cache_ref_count_entry(cache, hash, lock, cache_entry);
489}
490
491
493 dt_pixel_cache_entry_t *cache_entry)
494{
495 if(cache_entry == NULL)
496 cache_entry = dt_dev_pixelpipe_cache_get_entry(cache, hash);
497
498 if(cache_entry)
499 {
500 if(lock)
501 {
502 dt_pthread_rwlock_wrlock(&cache_entry->lock);
503 dt_pixel_cache_message(cache_entry, "write lock", TRUE);
504 }
505 else
506 {
507 dt_pthread_rwlock_unlock(&cache_entry->lock);
508 dt_pixel_cache_message(cache_entry, "write unlock", TRUE);
509 }
510 }
511}
512
513
515 dt_pixel_cache_entry_t *cache_entry)
516{
517 if(cache_entry == NULL)
518 cache_entry = dt_dev_pixelpipe_cache_get_entry(cache, hash);
519
520 if(cache_entry)
521 {
522 if(lock)
523 {
524 dt_pthread_rwlock_rdlock(&cache_entry->lock);
525 dt_pixel_cache_message(cache_entry, "read lock", TRUE);
526 }
527 else
528 {
529 dt_pthread_rwlock_unlock(&cache_entry->lock);
530 dt_pixel_cache_message(cache_entry, "read unlock", TRUE);
531 }
532 }
533}
534
535
537 dt_pixel_cache_entry_t *cache_entry)
538{
540 if(cache_entry == NULL)
541 cache_entry = _non_threadsafe_cache_get_entry(cache, hash);
542
543 if(cache_entry) cache_entry->auto_destroy = TRUE;
545}
546
547
549 dt_pixel_cache_entry_t *cache_entry)
550{
552 if(cache_entry == NULL)
553 cache_entry = _non_threadsafe_cache_get_entry(cache, hash);
554
555 if(cache_entry && cache_entry->auto_destroy && id == cache_entry->id)
556 {
557 cache->current_memory -= cache_entry->size;
558 g_hash_table_remove(cache->entries, GINT_TO_POINTER(hash));
559 }
561}
562
563
565{
566 if(!(darktable.unmuted & DT_DEBUG_PIPE)) return;
567
568 dt_print(DT_DEBUG_PIPE, "[pixelpipe] cache hit rate so far: %.3f%% - size: %lu MiB over %lu MiB - %i items\n", 100. * (cache->hits) / (float)cache->queries, cache->current_memory / (1024 * 1024), cache->max_memory / (1024 * 1024), g_hash_table_size(cache->entries));
569}
570
571// clang-format off
572// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
573// vim: shiftwidth=2 expandtab tabstop=2 cindent
574// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
575// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:191
#define TRUE
Definition ashift_lsd.c:151
#define FALSE
Definition ashift_lsd.c:147
int dt_atomic_get_int(dt_atomic_int *var)
Definition atomic.h:46
int dt_atomic_sub_int(dt_atomic_int *var, int decr)
Definition atomic.h:48
int dt_atomic_add_int(dt_atomic_int *var, int incr)
Definition atomic.h:47
atomic_int dt_atomic_int
Definition atomic.h:44
char * key
Definition common/metadata.c:40
char * name
Definition common/metadata.c:41
void dt_control_log(const char *msg,...)
Definition control.c:420
darktable_t darktable
Definition darktable.c:111
size_t dt_get_singlebuffer_mem()
Definition darktable.c:1568
size_t dt_get_available_mem()
Definition darktable.c:1555
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1415
@ DT_DEBUG_PIPE
Definition darktable.h:497
@ DT_DEBUG_VERBOSE
Definition darktable.h:499
#define dt_free_align(A)
Definition darktable.h:334
#define dt_pthread_rwlock_destroy
Definition dtpthread.h:336
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:319
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:304
#define dt_pthread_rwlock_wrlock
Definition dtpthread.h:339
#define dt_pthread_rwlock_t
Definition dtpthread.h:334
#define dt_pthread_rwlock_trywrlock
Definition dtpthread.h:341
static int dt_pthread_mutex_destroy(dt_pthread_mutex_t *mutex)
Definition dtpthread.h:324
#define dt_pthread_rwlock_init
Definition dtpthread.h:335
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:337
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:309
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:338
size_t size
Definition mipmap_cache.c:3
int dt_dev_pixelpipe_cache_remove(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const gboolean force, dt_pixel_cache_entry_t *cache_entry)
Arbitrarily remove the cache entry matching hash. Entries having a reference count > 0 (inter-thread ...
Definition pixelpipe_cache.c:113
void dt_dev_pixelpipe_cache_ref_count_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
Definition pixelpipe_cache.c:483
size_t dt_pixel_cache_get_size(dt_pixel_cache_entry_t *cache_entry)
Definition pixelpipe_cache.c:62
void dt_dev_pixelpipe_cache_rdlock_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
Definition pixelpipe_cache.c:514
void dt_dev_pixelpipe_cache_cleanup(dt_dev_pixelpipe_cache_t *cache)
Definition pixelpipe_cache.c:287
void _cache_get_oldest(gpointer key, gpointer value, gpointer user_data)
Definition pixelpipe_cache.c:131
dt_dev_pixelpipe_cache_t * dt_dev_pixelpipe_cache_init(size_t max_memory)
Definition pixelpipe_cache.c:275
void dt_dev_pixelpipe_cache_wrlock_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
Definition pixelpipe_cache.c:492
void dt_dev_pixelpipe_cache_print(dt_dev_pixelpipe_cache_t *cache)
Definition pixelpipe_cache.c:564
void dt_dev_pixelpipe_cache_flush(dt_dev_pixelpipe_cache_t *cache, const int id)
Remove cache lines matching id. Entries locked in read/write or having reference count greater than 0...
Definition pixelpipe_cache.c:397
void dt_dev_pixel_pipe_cache_auto_destroy_apply(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const int id, dt_pixel_cache_entry_t *cache_entry)
Definition pixelpipe_cache.c:548
static int _non_thread_safe_pixel_pipe_cache_remove_lru(dt_dev_pixelpipe_cache_t *cache)
Definition pixelpipe_cache.c:168
uint64_t dt_dev_pixelpipe_cache_get_hash_data(dt_dev_pixelpipe_cache_t *cache, void *data, dt_pixel_cache_entry_t **entry)
Find the hash of the cache entry holding the buffer data.
Definition pixelpipe_cache.c:435
static dt_pixel_cache_entry_t * dt_pixel_cache_new_entry(const uint64_t hash, const size_t size, const dt_iop_buffer_dsc_t dsc, const char *name, const int id, dt_dev_pixelpipe_cache_t *cache)
Definition pixelpipe_cache.c:196
void dt_dev_pixelpipe_cache_flag_auto_destroy(dt_dev_pixelpipe_cache_t *cache, uint64_t hash, dt_pixel_cache_entry_t *cache_entry)
Flag the cache entry matching hash as "auto_destroy". This is useful for short-lived/disposable cache...
Definition pixelpipe_cache.c:536
int dt_dev_pixel_pipe_cache_remove_lru(dt_dev_pixelpipe_cache_t *cache)
Definition pixelpipe_cache.c:187
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_get_entry_from_data(dt_dev_pixelpipe_cache_t *cache, void *data)
Return a reference to the cache entry holding the data buffer, or NULL if not found.
Definition pixelpipe_cache.c:444
int dt_dev_pixelpipe_cache_get_existing(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, void **data, dt_iop_buffer_dsc_t **dsc, dt_pixel_cache_entry_t **entry)
Get an existing cache line from the cache. This is similar to dt_dev_pixelpipe_cache_get,...
Definition pixelpipe_cache.c:351
static void _free_cache_entry(dt_pixel_cache_entry_t *cache_entry)
Definition pixelpipe_cache.c:263
dt_pixel_cache_entry_t * dt_dev_pixelpipe_cache_get_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Get an internal reference to the cache entry matching hash. If you are going to access this entry mor...
Definition pixelpipe_cache.c:53
dt_pixel_cache_entry_t * _non_threadsafe_cache_get_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash)
Definition pixelpipe_cache.c:47
void _non_thread_safe_cache_ref_count_entry(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Definition pixelpipe_cache.c:461
void dt_pixel_cache_message(dt_pixel_cache_entry_t *cache_entry, const char *message, gboolean verbose)
Definition pixelpipe_cache.c:68
int _non_thread_safe_cache_remove(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const gboolean force, dt_pixel_cache_entry_t *cache_entry)
Definition pixelpipe_cache.c:80
gboolean _for_each_remove(gpointer key, gpointer value, gpointer user_data)
Definition pixelpipe_cache.c:383
uint64_t _non_thread_safe_cache_get_hash_data(dt_dev_pixelpipe_cache_t *cache, void *data, dt_pixel_cache_entry_t **entry)
Definition pixelpipe_cache.c:412
int dt_dev_pixelpipe_cache_get(dt_dev_pixelpipe_cache_t *cache, const uint64_t hash, const size_t size, const char *name, const int id, void **data, dt_iop_buffer_dsc_t **dsc, dt_pixel_cache_entry_t **entry)
Get a cache line from the cache.
Definition pixelpipe_cache.c:296
Pixelpipe cache for storing intermediate results in the pixelpipe.
unsigned __int64 uint64_t
Definition strptime.c:71
Definition pixelpipe_cache.c:406
size_t size
Definition pixelpipe_cache.c:408
void * data
Definition pixelpipe_cache.c:407
Definition pixelpipe_cache.c:123
int64_t max_age
Definition pixelpipe_cache.c:124
dt_pixel_cache_entry_t * cache_entry
Definition pixelpipe_cache.c:126
uint64_t hash
Definition pixelpipe_cache.c:125
struct dt_dev_pixelpipe_cache_t * pixelpipe_cache
Definition darktable.h:556
int32_t unmuted
Definition darktable.h:526
Definition pixelpipe_cache.h:40
size_t current_memory
Definition pixelpipe_cache.h:45
GHashTable * entries
Definition pixelpipe_cache.h:41
dt_pthread_mutex_t lock
Definition pixelpipe_cache.h:46
size_t max_memory
Definition pixelpipe_cache.h:44
uint64_t queries
Definition pixelpipe_cache.h:42
uint64_t hits
Definition pixelpipe_cache.h:43
Definition develop/format.h:36
Definition pixelpipe_cache.c:30
uint64_t hash
Definition pixelpipe_cache.c:31
gboolean auto_destroy
Definition pixelpipe_cache.c:40
dt_atomic_int refcount
Definition pixelpipe_cache.c:38
void * data
Definition pixelpipe_cache.c:32
dt_iop_buffer_dsc_t dsc
Definition pixelpipe_cache.c:34
size_t size
Definition pixelpipe_cache.c:33
int64_t age
Definition pixelpipe_cache.c:35
dt_pthread_rwlock_t lock
Definition pixelpipe_cache.c:39
char * name
Definition pixelpipe_cache.c:36
int id
Definition pixelpipe_cache.c:37
#define dt_alloc_align(B)
Definition tests/cache.c:22
#define MIN(a, b)
Definition thinplate.c:23
#define MAX(a, b)
Definition thinplate.c:20