~ubuntu-branches/ubuntu/vivid/gimp/vivid

« back to all changes in this revision

Viewing changes to app/base/tile-cache.c

  • Committer: Package Import Robot
  • Author(s): Jordi Mallach
  • Date: 2012-05-08 18:50:03 UTC
  • mto: (1.1.26) (0.5.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 71.
  • Revision ID: package-import@ubuntu.com-20120508185003-tltkvbaysf8d2426
ImportĀ upstreamĀ versionĀ 2.8.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* GIMP - The GNU Image Manipulation Program
2
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3
3
 *
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.
8
8
 *
9
9
 * This program is distributed in the hope that it will be useful,
12
12
 * GNU General Public License for more details.
13
13
 *
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/>.
17
16
 */
18
17
 
19
18
#include "config.h"
29
28
#include "tile-private.h"
30
29
 
31
30
 
32
 
#define IDLE_SWAPPER_TIMEOUT  250
33
 
 
34
 
 
35
 
static gboolean  tile_cache_zorch_next     (void);
36
 
static void      tile_cache_flush_internal (Tile     *tile);
37
 
 
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
39
34
 
40
35
 
41
36
typedef struct _TileList
44
39
  Tile *last;
45
40
} TileList;
46
41
 
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 };
 
42
 
 
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;
54
50
 
 
51
#ifdef TILE_PROFILING
 
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;
 
58
#endif
55
59
 
56
60
#ifdef ENABLE_MP
57
61
 
67
71
 
68
72
#endif
69
73
 
 
74
#define PENDING_WRITE(t) ((t)->dirty || (t)->swap_offset == -1)
 
75
 
 
76
 
 
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);
 
80
#ifdef TILE_PROFILING
 
81
static void      tile_verify               (void);
 
82
#endif
 
83
 
70
84
 
71
85
void
72
 
tile_cache_init (gulong tile_cache_size)
 
86
tile_cache_init (guint64 tile_cache_size)
73
87
{
74
88
#ifdef ENABLE_MP
75
89
  g_return_if_fail (tile_cache_mutex == NULL);
77
91
  tile_cache_mutex = g_mutex_new ();
78
92
#endif
79
93
 
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;
82
96
 
83
97
  max_cache_size = tile_cache_size;
84
98
}
93
107
    }
94
108
 
95
109
  if (cur_cache_size > 0)
96
 
    g_warning ("tile cache not empty (%ld bytes left)", cur_cache_size);
 
110
    g_warning ("tile cache not empty (%"G_GUINT64_FORMAT" bytes left)",
 
111
               cur_cache_size);
97
112
 
98
113
  tile_cache_set_size (0);
99
114
 
104
119
}
105
120
 
106
121
void
 
122
tile_cache_suspend_idle_swapper(void)
 
123
{
 
124
  idle_delay = 1;
 
125
}
 
126
 
 
127
void
107
128
tile_cache_insert (Tile *tile)
108
129
{
109
 
  TileList *list;
110
 
  TileList *newlist;
111
130
 
112
131
  TILE_CACHE_LOCK;
113
132
 
120
139
   *  it was the most recently accessed tile.
121
140
   */
122
141
 
123
 
  list = tile->listhead;
124
 
 
125
 
  newlist = ((tile->dirty || tile->swap_offset == -1) ?
126
 
             &dirty_list : &clean_list);
127
 
 
128
 
  /* if list is NULL, the tile is not in the cache */
129
 
 
130
 
  if (list)
 
142
  if (tile->cached)
131
143
    {
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. */
134
145
 
135
146
      if (tile->next)
136
147
        tile->next->prev = tile->prev;
137
148
      else
138
 
        list->last = tile->prev;
139
 
 
140
 
      if (tile->prev)
141
 
        tile->prev->next = tile->next;
142
 
      else
143
 
        list->first = tile->next;
144
 
 
145
 
      tile->listhead = NULL;
146
 
 
147
 
      if (list == &dirty_list)
148
 
        cur_cache_dirty -= tile->size;
 
149
        tile_list.last = tile->prev;
 
150
 
 
151
      if(tile->prev){
 
152
        tile->prev->next = tile->next;
 
153
      }else{
 
154
        tile_list.first = tile->next;
 
155
      }
 
156
 
 
157
      if (PENDING_WRITE(tile))
 
158
        cur_cache_dirty -= tile->size;
 
159
 
 
160
      if(tile == idle_scan_last)
 
161
        idle_scan_last = tile->next;
 
162
 
149
163
    }
150
164
  else
151
165
    {
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.
157
171
       */
158
 
      while ((cur_cache_size + max_tile_size) > max_cache_size)
 
172
 
 
173
#ifdef TILE_PROFILING
 
174
      if ((cur_cache_size + tile->size) > max_cache_size)
159
175
        {
160
 
          if (! tile_cache_zorch_next ())
161
 
            {
162
 
              g_warning ("cache: unable to find room for a tile");
163
 
              goto out;
 
176
          GTimeVal now;
 
177
          GTimeVal later;
 
178
 
 
179
          g_get_current_time(&now);
 
180
#endif
 
181
          while ((cur_cache_size + tile->size) > max_cache_size)
 
182
            {
 
183
              if (! tile_cache_zorch_next ())
 
184
                {
 
185
                  g_warning ("cache: unable to find room for a tile");
 
186
                  goto out;
 
187
                }
 
188
            }
 
189
 
 
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;
 
194
 
 
195
          if (tile_total_interactive_usec < 0)
 
196
            {
 
197
              tile_total_interactive_usec += 1000000;
 
198
              tile_total_interactive_sec--;
 
199
            }
 
200
 
 
201
          if (tile_total_interactive_usec > 1000000)
 
202
            {
 
203
              tile_total_interactive_usec -= 1000000;
 
204
              tile_total_interactive_sec++;
164
205
            }
165
206
        }
 
207
#endif
166
208
 
167
209
      cur_cache_size += tile->size;
168
210
    }
170
212
  /* Put the tile at the end of the proper list */
171
213
 
172
214
  tile->next = NULL;
173
 
  tile->prev = newlist->last;
174
 
  tile->listhead = newlist;
 
215
  tile->prev = tile_list.last;
175
216
 
176
 
  if (newlist->last)
177
 
    newlist->last->next = tile;
 
217
  if (tile_list.last)
 
218
    tile_list.last->next = tile;
178
219
  else
179
 
    newlist->first = tile;
180
 
 
181
 
  newlist->last = tile;
182
 
 
183
 
  if (tile->dirty || (tile->swap_offset == -1))
 
220
    tile_list.first = tile;
 
221
 
 
222
  tile_list.last = tile;
 
223
  tile->cached = TRUE;
 
224
  idle_delay = 1;
 
225
 
 
226
  if (PENDING_WRITE(tile))
184
227
    {
185
228
      cur_cache_dirty += tile->size;
186
229
 
187
 
      if (! idle_swapper &&
188
 
          cur_cache_dirty * 2 > max_cache_size)
 
230
      if (! idle_scan_last)
 
231
        idle_scan_last=tile;
 
232
 
 
233
      if (! idle_swapper)
189
234
        {
 
235
#ifdef TILE_PROFILING
 
236
          g_printerr("idle swapper -> started\n");
 
237
          g_printerr("idle swapper -> waiting");
 
238
#endif
 
239
          idle_delay = 0;
190
240
          idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
191
 
                                             IDLE_SWAPPER_TIMEOUT,
 
241
                                             IDLE_SWAPPER_START,
192
242
                                             tile_idle_preswap,
193
243
                                             NULL, NULL);
194
244
        }
203
253
{
204
254
  TILE_CACHE_LOCK;
205
255
 
206
 
  tile_cache_flush_internal (tile);
 
256
  if (tile->cached)
 
257
    tile_cache_flush_internal (tile);
207
258
 
208
259
  TILE_CACHE_UNLOCK;
209
260
}
210
261
 
211
262
void
212
 
tile_cache_set_size (gulong cache_size)
 
263
tile_cache_set_size (guint64 cache_size)
213
264
{
214
265
  TILE_CACHE_LOCK;
215
266
 
 
267
  idle_delay = 1;
216
268
  max_cache_size = cache_size;
217
269
 
218
270
  while (cur_cache_size > max_cache_size)
227
279
static void
228
280
tile_cache_flush_internal (Tile *tile)
229
281
{
230
 
  TileList *list = tile->listhead;
231
 
 
232
 
  /* Find where the tile is in the cache.
233
 
   */
234
 
 
235
 
  if (list)
236
 
    {
237
 
      cur_cache_size -= tile->size;
238
 
 
239
 
      if (list == &dirty_list)
240
 
        cur_cache_dirty -= tile->size;
241
 
 
242
 
      if (tile->next)
243
 
        tile->next->prev = tile->prev;
244
 
      else
245
 
        list->last = tile->prev;
246
 
 
247
 
      if (tile->prev)
248
 
        tile->prev->next = tile->next;
249
 
      else
250
 
        list->first = tile->next;
251
 
 
252
 
      tile->listhead = NULL;
253
 
    }
 
282
 
 
283
  tile->cached = FALSE;
 
284
 
 
285
  if (PENDING_WRITE(tile))
 
286
    cur_cache_dirty -= tile->size;
 
287
 
 
288
  cur_cache_size -= tile->size;
 
289
 
 
290
  if (tile->next)
 
291
    tile->next->prev = tile->prev;
 
292
  else
 
293
    tile_list.last = tile->prev;
 
294
 
 
295
  if (tile->prev)
 
296
    tile->prev->next = tile->next;
 
297
  else
 
298
    tile_list.first = tile->next;
 
299
 
 
300
  if (tile == idle_scan_last)
 
301
    idle_scan_last = tile->next;
 
302
 
 
303
  tile->next = tile->prev = NULL;
254
304
}
255
305
 
256
306
static gboolean
257
307
tile_cache_zorch_next (void)
258
308
{
259
 
  Tile *tile;
260
 
 
261
 
  if (clean_list.first)
262
 
    tile = clean_list.first;
263
 
  else if (dirty_list.first)
264
 
    tile = dirty_list.first;
265
 
  else
 
309
 
 
310
  Tile *tile = tile_list.first;
 
311
 
 
312
  if (! tile)
266
313
    return FALSE;
267
314
 
 
315
#ifdef TILE_PROFILING
 
316
  tile_total_zorched++;
 
317
  tile->zorched = TRUE;
 
318
 
 
319
  if (PENDING_WRITE (tile))
 
320
    {
 
321
      tile_total_zorched_swapout++;
 
322
      tile->zorchout = TRUE;
 
323
    }
 
324
#endif
 
325
 
268
326
  tile_cache_flush_internal (tile);
269
327
 
270
 
  if (tile->dirty || tile->swap_offset == -1)
 
328
  if (PENDING_WRITE (tile))
271
329
    {
 
330
      idle_delay = 1;
272
331
      tile_swap_out (tile);
273
332
    }
274
333
 
277
336
      g_free (tile->data);
278
337
      tile->data = NULL;
279
338
 
 
339
#ifdef TILE_PROFILING
 
340
      tile_exist_count--;
 
341
#endif
280
342
      return TRUE;
281
343
    }
282
344
 
285
347
}
286
348
 
287
349
static gboolean
 
350
tile_idle_preswap_run (gpointer data)
 
351
{
 
352
  Tile *tile;
 
353
  int count = 0;
 
354
 
 
355
  if (idle_delay)
 
356
    {
 
357
#ifdef TILE_PROFILING
 
358
      g_printerr("\nidle swapper -> waiting");
 
359
#endif
 
360
 
 
361
      idle_delay = 0;
 
362
      idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
 
363
                                         IDLE_SWAPPER_START,
 
364
                                         tile_idle_preswap,
 
365
                                         NULL, NULL);
 
366
      return FALSE;
 
367
    }
 
368
 
 
369
  TILE_CACHE_LOCK;
 
370
 
 
371
#ifdef TILE_PROFILING
 
372
  g_printerr(".");
 
373
#endif
 
374
 
 
375
  tile = idle_scan_last;
 
376
 
 
377
  while (tile)
 
378
    {
 
379
      if (PENDING_WRITE (tile))
 
380
        {
 
381
          idle_scan_last = tile->next;
 
382
 
 
383
#ifdef TILE_PROFILING
 
384
          tile_idle_swapout++;
 
385
#endif
 
386
          tile_swap_out (tile);
 
387
 
 
388
          if (! PENDING_WRITE(tile))
 
389
            cur_cache_dirty -= tile->size;
 
390
 
 
391
          count++;
 
392
          if (count >= IDLE_SWAPPER_TILES_PER_INTERVAL)
 
393
            {
 
394
              TILE_CACHE_UNLOCK;
 
395
              return TRUE;
 
396
            }
 
397
        }
 
398
 
 
399
      tile = tile->next;
 
400
    }
 
401
 
 
402
#ifdef TILE_PROFILING
 
403
  g_printerr ("\nidle swapper -> stopped\n");
 
404
#endif
 
405
 
 
406
  idle_scan_last = NULL;
 
407
  idle_swapper = 0;
 
408
 
 
409
#ifdef TILE_PROFILING
 
410
  tile_verify ();
 
411
#endif
 
412
 
 
413
  TILE_CACHE_UNLOCK;
 
414
 
 
415
  return FALSE;
 
416
}
 
417
 
 
418
static gboolean
288
419
tile_idle_preswap (gpointer data)
289
420
{
290
 
  Tile *tile;
291
 
 
292
 
  if (cur_cache_dirty * 2 < max_cache_size)
293
 
    {
294
 
      idle_swapper = 0;
295
 
      return FALSE;
296
 
    }
297
 
 
298
 
  TILE_CACHE_LOCK;
299
 
 
300
 
  if ((tile = dirty_list.first))
301
 
    {
302
 
      tile_swap_out (tile);
303
 
 
304
 
      dirty_list.first = tile->next;
305
 
 
306
 
      if (tile->next)
307
 
        tile->next->prev = NULL;
308
 
      else
309
 
        dirty_list.last = NULL;
310
 
 
311
 
      tile->next = NULL;
312
 
      tile->prev = clean_list.last;
313
 
      tile->listhead = &clean_list;
314
 
 
315
 
      if (clean_list.last)
316
 
        clean_list.last->next = tile;
317
 
      else
318
 
        clean_list.first = tile;
319
 
 
320
 
      clean_list.last = tile;
321
 
      cur_cache_dirty -= tile->size;
322
 
    }
323
 
 
324
 
  TILE_CACHE_UNLOCK;
325
 
 
326
 
  return TRUE;
327
 
}
 
421
 
 
422
  if (idle_delay){
 
423
#ifdef TILE_PROFILING
 
424
    g_printerr(".");
 
425
#endif
 
426
    idle_delay = 0;
 
427
    return TRUE;
 
428
  }
 
429
 
 
430
#ifdef TILE_PROFILING
 
431
  tile_verify ();
 
432
  g_printerr("\nidle swapper -> running");
 
433
#endif
 
434
 
 
435
  idle_swapper = g_timeout_add_full (G_PRIORITY_LOW,
 
436
                                     IDLE_SWAPPER_INTERVAL_MS,
 
437
                                     tile_idle_preswap_run,
 
438
                                     NULL, NULL);
 
439
  return FALSE;
 
440
}
 
441
 
 
442
#ifdef TILE_PROFILING
 
443
static void
 
444
tile_verify (void)
 
445
{
 
446
  /* scan list linearly, count metrics, compare to running totals */
 
447
  const Tile *t;
 
448
  guint64     local_size  = 0;
 
449
  guint64     local_dirty = 0;
 
450
  guint64     acc         = 0;
 
451
 
 
452
  for (t = tile_list.first; t; t = t->next)
 
453
    {
 
454
      local_size += t->size;
 
455
 
 
456
      if (PENDING_WRITE (t))
 
457
        local_dirty += t->size;
 
458
    }
 
459
 
 
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);
 
464
 
 
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);
 
469
 
 
470
  /* scan forward from scan list */
 
471
  for (t = idle_scan_last; t; t = t->next)
 
472
    {
 
473
      if (PENDING_WRITE (t))
 
474
        acc += t->size;
 
475
    }
 
476
 
 
477
  if (acc != local_dirty)
 
478
    g_printerr ("\nDirty scan follower mismatch: running=%"G_GUINT64_FORMAT
 
479
                ", tested=%"G_GUINT64_FORMAT"\n",
 
480
                acc,local_dirty);
 
481
}
 
482
#endif