1
1
/* GIMP - The GNU Image Manipulation Program
2
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
4
* This program is free software; you can redistribute it and/or modify
4
* This program is free software: you can redistribute it and/or modify
5
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
6
* the Free Software Foundation; either version 3 of the License, or
7
7
* (at your option) any later version.
9
9
* This program is distributed in the hope that it will be useful,
12
12
* GNU General Public License for more details.
14
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
18
#include "config.h"
29
28
#include "tile-private.h"
32
#define IDLE_SWAPPER_TIMEOUT 250
35
static gboolean tile_cache_zorch_next (void);
36
static void tile_cache_flush_internal (Tile *tile);
38
static gboolean tile_idle_preswap (gpointer data);
31
#define IDLE_SWAPPER_START 1000
32
#define IDLE_SWAPPER_INTERVAL_MS 20
33
#define IDLE_SWAPPER_TILES_PER_INTERVAL 10
41
36
typedef struct _TileList
47
static const gulong max_tile_size = TILE_WIDTH * TILE_HEIGHT * 4;
48
static gulong cur_cache_size = 0;
49
static gulong max_cache_size = 0;
50
static gulong cur_cache_dirty = 0;
51
static TileList clean_list = { NULL, NULL };
52
static TileList dirty_list = { NULL, NULL };
43
static guint64 cur_cache_size = 0;
44
static guint64 max_cache_size = 0;
45
static guint64 cur_cache_dirty = 0;
46
static TileList tile_list = { NULL, NULL };
53
47
static guint idle_swapper = 0;
48
static guint idle_delay = 0;
49
static Tile *idle_scan_last = NULL;
52
extern gulong tile_idle_swapout;
53
extern gulong tile_total_zorched;
54
extern gulong tile_total_zorched_swapout;
55
extern glong tile_total_interactive_sec;
56
extern glong tile_total_interactive_usec;
57
extern gint tile_exist_count;
74
#define PENDING_WRITE(t) ((t)->dirty || (t)->swap_offset == -1)
77
static gboolean tile_cache_zorch_next (void);
78
static void tile_cache_flush_internal (Tile *tile);
79
static gboolean tile_idle_preswap (gpointer data);
81
static void tile_verify (void);
72
tile_cache_init (gulong tile_cache_size)
86
tile_cache_init (guint64 tile_cache_size)
75
89
g_return_if_fail (tile_cache_mutex == NULL);
77
91
tile_cache_mutex = g_mutex_new ();
80
clean_list.first = clean_list.last = NULL;
81
dirty_list.first = dirty_list.last = NULL;
94
tile_list.first = tile_list.last = NULL;
95
idle_scan_last = NULL;
83
97
max_cache_size = tile_cache_size;
120
139
* it was the most recently accessed tile.
123
list = tile->listhead;
125
newlist = ((tile->dirty || tile->swap_offset == -1) ?
126
&dirty_list : &clean_list);
128
/* if list is NULL, the tile is not in the cache */
132
/* Tile is in the cache. Remove it from its current list and
133
put it at the tail of the proper list (clean or dirty) */
144
/* Tile is in the cache. Remove it from the list. */
136
147
tile->next->prev = tile->prev;
138
list->last = tile->prev;
141
tile->prev->next = tile->next;
143
list->first = tile->next;
145
tile->listhead = NULL;
147
if (list == &dirty_list)
148
cur_cache_dirty -= tile->size;
149
tile_list.last = tile->prev;
152
tile->prev->next = tile->next;
154
tile_list.first = tile->next;
157
if (PENDING_WRITE(tile))
158
cur_cache_dirty -= tile->size;
160
if(tile == idle_scan_last)
161
idle_scan_last = tile->next;
155
169
* cache is smaller than the size of a tile in which case
156
170
* it won't be possible to put it in the cache.
158
while ((cur_cache_size + max_tile_size) > max_cache_size)
173
#ifdef TILE_PROFILING
174
if ((cur_cache_size + tile->size) > max_cache_size)
160
if (! tile_cache_zorch_next ())
162
g_warning ("cache: unable to find room for a tile");
179
g_get_current_time(&now);
181
while ((cur_cache_size + tile->size) > max_cache_size)
183
if (! tile_cache_zorch_next ())
185
g_warning ("cache: unable to find room for a tile");
190
#ifdef TILE_PROFILING
191
g_get_current_time (&later);
192
tile_total_interactive_usec += later.tv_usec - now.tv_usec;
193
tile_total_interactive_sec += later.tv_sec - now.tv_sec;
195
if (tile_total_interactive_usec < 0)
197
tile_total_interactive_usec += 1000000;
198
tile_total_interactive_sec--;
201
if (tile_total_interactive_usec > 1000000)
203
tile_total_interactive_usec -= 1000000;
204
tile_total_interactive_sec++;
167
209
cur_cache_size += tile->size;
170
212
/* Put the tile at the end of the proper list */
172
214
tile->next = NULL;
173
tile->prev = newlist->last;
174
tile->listhead = newlist;
215
tile->prev = tile_list.last;
177
newlist->last->next = tile;
218
tile_list.last->next = tile;
179
newlist->first = tile;
181
newlist->last = tile;
183
if (tile->dirty || (tile->swap_offset == -1))
220
tile_list.first = tile;
222
tile_list.last = tile;
226
if (PENDING_WRITE(tile))
185
228
cur_cache_dirty += tile->size;
187
if (! idle_swapper &&
188
cur_cache_dirty * 2 > max_cache_size)
230
if (! idle_scan_last)
235
#ifdef TILE_PROFILING
236
g_printerr("idle swapper -> started\n");
237
g_printerr("idle swapper -> waiting");
190
240
idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
191
IDLE_SWAPPER_TIMEOUT,
192
242
tile_idle_preswap,
206
tile_cache_flush_internal (tile);
257
tile_cache_flush_internal (tile);
208
259
TILE_CACHE_UNLOCK;
212
tile_cache_set_size (gulong cache_size)
263
tile_cache_set_size (guint64 cache_size)
216
268
max_cache_size = cache_size;
218
270
while (cur_cache_size > max_cache_size)
228
280
tile_cache_flush_internal (Tile *tile)
230
TileList *list = tile->listhead;
232
/* Find where the tile is in the cache.
237
cur_cache_size -= tile->size;
239
if (list == &dirty_list)
240
cur_cache_dirty -= tile->size;
243
tile->next->prev = tile->prev;
245
list->last = tile->prev;
248
tile->prev->next = tile->next;
250
list->first = tile->next;
252
tile->listhead = NULL;
283
tile->cached = FALSE;
285
if (PENDING_WRITE(tile))
286
cur_cache_dirty -= tile->size;
288
cur_cache_size -= tile->size;
291
tile->next->prev = tile->prev;
293
tile_list.last = tile->prev;
296
tile->prev->next = tile->next;
298
tile_list.first = tile->next;
300
if (tile == idle_scan_last)
301
idle_scan_last = tile->next;
303
tile->next = tile->prev = NULL;
257
307
tile_cache_zorch_next (void)
261
if (clean_list.first)
262
tile = clean_list.first;
263
else if (dirty_list.first)
264
tile = dirty_list.first;
310
Tile *tile = tile_list.first;
315
#ifdef TILE_PROFILING
316
tile_total_zorched++;
317
tile->zorched = TRUE;
319
if (PENDING_WRITE (tile))
321
tile_total_zorched_swapout++;
322
tile->zorchout = TRUE;
268
326
tile_cache_flush_internal (tile);
270
if (tile->dirty || tile->swap_offset == -1)
328
if (PENDING_WRITE (tile))
272
331
tile_swap_out (tile);
350
tile_idle_preswap_run (gpointer data)
357
#ifdef TILE_PROFILING
358
g_printerr("\nidle swapper -> waiting");
362
idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
371
#ifdef TILE_PROFILING
375
tile = idle_scan_last;
379
if (PENDING_WRITE (tile))
381
idle_scan_last = tile->next;
383
#ifdef TILE_PROFILING
386
tile_swap_out (tile);
388
if (! PENDING_WRITE(tile))
389
cur_cache_dirty -= tile->size;
392
if (count >= IDLE_SWAPPER_TILES_PER_INTERVAL)
402
#ifdef TILE_PROFILING
403
g_printerr ("\nidle swapper -> stopped\n");
406
idle_scan_last = NULL;
409
#ifdef TILE_PROFILING
288
419
tile_idle_preswap (gpointer data)
292
if (cur_cache_dirty * 2 < max_cache_size)
300
if ((tile = dirty_list.first))
302
tile_swap_out (tile);
304
dirty_list.first = tile->next;
307
tile->next->prev = NULL;
309
dirty_list.last = NULL;
312
tile->prev = clean_list.last;
313
tile->listhead = &clean_list;
316
clean_list.last->next = tile;
318
clean_list.first = tile;
320
clean_list.last = tile;
321
cur_cache_dirty -= tile->size;
423
#ifdef TILE_PROFILING
430
#ifdef TILE_PROFILING
432
g_printerr("\nidle swapper -> running");
435
idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
436
IDLE_SWAPPER_INTERVAL_MS,
437
tile_idle_preswap_run,
442
#ifdef TILE_PROFILING
446
/* scan list linearly, count metrics, compare to running totals */
448
guint64 local_size = 0;
449
guint64 local_dirty = 0;
452
for (t = tile_list.first; t; t = t->next)
454
local_size += t->size;
456
if (PENDING_WRITE (t))
457
local_dirty += t->size;
460
if (local_size != cur_cache_size)
461
g_printerr ("\nCache size mismatch: running=%"G_GUINT64_FORMAT
462
", tested=%"G_GUINT64_FORMAT"\n",
463
cur_cache_size,local_size);
465
if (local_dirty != cur_cache_dirty)
466
g_printerr ("\nCache dirty mismatch: running=%"G_GUINT64_FORMAT
467
", tested=%"G_GUINT64_FORMAT"\n",
468
cur_cache_dirty,local_dirty);
470
/* scan forward from scan list */
471
for (t = idle_scan_last; t; t = t->next)
473
if (PENDING_WRITE (t))
477
if (acc != local_dirty)
478
g_printerr ("\nDirty scan follower mismatch: running=%"G_GUINT64_FORMAT
479
", tested=%"G_GUINT64_FORMAT"\n",