mirror of https://github.com/GNOME/gimp.git
app/tile_swap.c app/tile_swap.h app/tile_manager.c app/tile_manager.h
Sun Jul 12 19:00:15 EDT 1998 Michael K. Johnson <johnsonm@redhat.com> * app/tile_swap.c * app/tile_swap.h * app/tile_manager.c * app/tile_manager.h * app/pixel_region.c * app/pixel_region.h: asynchronous swapin on systems with pthreads. This version is not at all tuned, and the only interface which makes use of it now is pixel_region_{g,s}et_{row,col}. Other functions which know ahead of time the area that they will be needing can request that it be asynchronously swapped in via the pixel_region_get_async() function. Compiles and survives basic testing.
This commit is contained in:
parent
a178be4922
commit
d2e6afe08c
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
|||
Sun Jul 12 19:00:15 EDT 1998 Michael K. Johnson <johnsonm@redhat.com>
|
||||
|
||||
* app/tile_swap.c
|
||||
* app/tile_swap.h
|
||||
* app/tile_manager.c
|
||||
* app/tile_manager.h
|
||||
* app/pixel_region.c
|
||||
* app/pixel_region.h: asynchronous swapin on systems with
|
||||
pthreads. This version is not at all tuned, and the only
|
||||
interface which makes use of it now is
|
||||
pixel_region_{g,s}et_{row,col}. Other functions which
|
||||
know ahead of time the area that they will be needing can
|
||||
request that it be asynchronously swapped in via the
|
||||
pixel_region_get_async() function.
|
||||
|
||||
Sun Jul 12 17:32:24 1998 Scott Goehring <scott@poverty.bloomington.in.us>
|
||||
|
||||
* app/tile_cache.c: Some optimizations and bugfixes relative to
|
||||
|
|
|
@ -104,6 +104,22 @@ pixel_region_resize (PR, x, y, w, h)
|
|||
PR->h = h;
|
||||
}
|
||||
|
||||
/* request that tiles within a region be fetched asynchronously
|
||||
*/
|
||||
void
|
||||
pixel_region_get_async (PR, ulx, uly, lrx, lry)
|
||||
PixelRegion *PR;
|
||||
int ulx;
|
||||
int uly;
|
||||
int lrx;
|
||||
int lry;
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for (y = uly; y < lry; y += TILE_HEIGHT)
|
||||
for (x = ulx; x < lrx; x += TILE_WIDTH)
|
||||
tile_manager_get_async (PR->tiles, x, y, 0);
|
||||
}
|
||||
|
||||
void
|
||||
pixel_region_get_row (PR, x, y, w, data, subsample)
|
||||
|
@ -122,6 +138,8 @@ pixel_region_get_row (PR, x, y, w, data, subsample)
|
|||
|
||||
end = x + w;
|
||||
|
||||
pixel_region_get_async (PR, x, y, end, y);
|
||||
|
||||
while (x < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, FALSE);
|
||||
|
@ -156,6 +174,8 @@ pixel_region_set_row (PR, x, y, w, data)
|
|||
|
||||
end = x + w;
|
||||
|
||||
pixel_region_get_async (PR, x, y, end, y);
|
||||
|
||||
while (x < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, TRUE);
|
||||
|
@ -190,6 +210,8 @@ pixel_region_get_col (PR, x, y, h, data, subsample)
|
|||
|
||||
end = y + h;
|
||||
|
||||
pixel_region_get_async (PR, x, y, x, end);
|
||||
|
||||
while (y < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, FALSE);
|
||||
|
@ -225,6 +247,8 @@ pixel_region_set_col (PR, x, y, h, data)
|
|||
|
||||
end = y + h;
|
||||
|
||||
pixel_region_get_async (PR, x, y, x, end);
|
||||
|
||||
while (y < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, TRUE);
|
||||
|
|
|
@ -38,6 +38,8 @@ struct _PixelRegion
|
|||
/* PixelRegion functions */
|
||||
void pixel_region_init (PixelRegion *, TileManager *, int, int, int, int, int);
|
||||
void pixel_region_resize (PixelRegion *, int, int, int, int);
|
||||
void pixel_region_get_async (PixelRegion *PR, int ulx, int uly,
|
||||
int lrx, int lry);
|
||||
void pixel_region_get_row (PixelRegion *, int, int, int, unsigned char *, int);
|
||||
void pixel_region_set_row (PixelRegion *, int, int, int, unsigned char *);
|
||||
void pixel_region_get_col (PixelRegion *, int, int, int, unsigned char *, int);
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
static void tile_manager_destroy_level (TileManager *tm,
|
||||
TileLevel *level);
|
||||
static void tile_invalidate (Tile **tile_ptr, TileManager *tm, int tile_num);
|
||||
static int tile_manager_get_tile_num (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level);
|
||||
|
||||
|
||||
TileManager*
|
||||
|
@ -164,23 +168,12 @@ tile_manager_get_tile (TileManager *tm,
|
|||
int wantread,
|
||||
int wantwrite)
|
||||
{
|
||||
TileLevel *tile_level;
|
||||
int tile_row;
|
||||
int tile_col;
|
||||
int tile_num;
|
||||
|
||||
if ((level < 0) || (level >= tm->nlevels))
|
||||
tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel, level);
|
||||
if (tile_num < 0)
|
||||
return NULL;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
if ((xpixel < 0) || (xpixel >= tile_level->width) ||
|
||||
(ypixel < 0) || (ypixel >= tile_level->height))
|
||||
return NULL;
|
||||
|
||||
tile_row = ypixel / TILE_HEIGHT;
|
||||
tile_col = xpixel / TILE_WIDTH;
|
||||
tile_num = tile_row * tile_level->ntile_cols + tile_col;
|
||||
|
||||
return tile_manager_get (tm, tile_num, level, wantread, wantwrite);
|
||||
}
|
||||
|
||||
|
@ -273,6 +266,26 @@ tile_manager_get (TileManager *tm,
|
|||
return *tile_ptr;
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_get_async (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level)
|
||||
{
|
||||
Tile *tile_ptr;
|
||||
TileLevel *tile_level;
|
||||
int tile_num;
|
||||
|
||||
tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel, level);
|
||||
if (tile_num < 0)
|
||||
return;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
tile_ptr = tile_level->tiles[tile_num];
|
||||
|
||||
tile_swap_in_async (tile_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_validate (TileManager *tm,
|
||||
Tile *tile)
|
||||
|
@ -597,3 +610,29 @@ tile_manager_map (TileManager *tm,
|
|||
|
||||
/* printf("}");fflush(stdout);*/
|
||||
}
|
||||
|
||||
static int
|
||||
tile_manager_get_tile_num (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level)
|
||||
{
|
||||
TileLevel *tile_level;
|
||||
int tile_row;
|
||||
int tile_col;
|
||||
int tile_num;
|
||||
|
||||
if ((level < 0) || (level >= tm->nlevels))
|
||||
return -1;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
if ((xpixel < 0) || (xpixel >= tile_level->width) ||
|
||||
(ypixel < 0) || (ypixel >= tile_level->height))
|
||||
return -1;
|
||||
|
||||
tile_row = ypixel / TILE_HEIGHT;
|
||||
tile_col = xpixel / TILE_WIDTH;
|
||||
tile_num = tile_row * tile_level->ntile_cols + tile_col;
|
||||
|
||||
return tile_num;
|
||||
}
|
||||
|
|
|
@ -93,6 +93,16 @@ Tile* tile_manager_get (TileManager *tm,
|
|||
int wantread,
|
||||
int wantwrite);
|
||||
|
||||
/* Request that (if possible) the tile at x,y,layer be swapped
|
||||
* in. This is only a hint to improve performance; no guarantees.
|
||||
* The tile may be swapped in or otherwise made more accessible
|
||||
* if it is convenient...
|
||||
*/
|
||||
void tile_manager_get_async (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level);
|
||||
|
||||
void tile_manager_map_tile (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
|
||||
#include "tile_pvt.h" /* ick. */
|
||||
|
||||
typedef struct _SwapFile SwapFile;
|
||||
typedef struct _DefSwapFile DefSwapFile;
|
||||
typedef struct _Gap Gap;
|
||||
typedef struct _SwapFile SwapFile;
|
||||
typedef struct _DefSwapFile DefSwapFile;
|
||||
typedef struct _Gap Gap;
|
||||
typedef struct _AsyncSwapArgs AsyncSwapArgs;
|
||||
|
||||
struct _SwapFile
|
||||
{
|
||||
|
@ -41,6 +42,13 @@ struct _Gap
|
|||
long end;
|
||||
};
|
||||
|
||||
struct _AsyncSwapInfo
|
||||
{
|
||||
DefSwapFile *def_swap_file;
|
||||
int fd;
|
||||
Tile *tile;
|
||||
};
|
||||
|
||||
|
||||
static void tile_swap_init (void);
|
||||
static guint tile_swap_hash (int *key);
|
||||
|
@ -57,6 +65,9 @@ static int tile_swap_default (int fd,
|
|||
static void tile_swap_default_in (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
static void tile_swap_default_in_async (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
static void tile_swap_default_out (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
|
@ -72,6 +83,9 @@ static void tile_swap_resize (DefSwapFile *def_swap_file,
|
|||
static Gap* tile_swap_gap_new (long start,
|
||||
long end);
|
||||
static void tile_swap_gap_destroy (Gap *gap);
|
||||
#ifdef USE_PTHREADS
|
||||
static void* tile_swap_in_thread (void *);
|
||||
#endif
|
||||
|
||||
|
||||
static int initialize = TRUE;
|
||||
|
@ -82,6 +96,13 @@ static int next_swap_num = 1;
|
|||
static long swap_file_grow = 16 * TILE_WIDTH * TILE_HEIGHT * 4;
|
||||
#ifdef USE_PTHREADS
|
||||
static pthread_mutex_t swapfile_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* async_swapin_mutex protects only the list, not the tiles therein */
|
||||
static pthread_t swapin_thread;
|
||||
static pthread_mutex_t async_swapin_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t async_swapin_signal = PTHREAD_COND_INITIALIZER;
|
||||
static GSList *async_swapin_tiles = NULL;
|
||||
static GSList *async_swapin_tiles_end = NULL;
|
||||
#endif
|
||||
|
||||
static gboolean seek_err_msg = TRUE, read_err_msg = TRUE, write_err_msg = TRUE;
|
||||
|
@ -207,6 +228,15 @@ out:
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tile_swap_in_async (Tile *tile)
|
||||
{
|
||||
if (tile->swap_offset == -1)
|
||||
return;
|
||||
|
||||
tile_swap_command (tile, SWAP_IN_ASYNC);
|
||||
}
|
||||
|
||||
void
|
||||
tile_swap_in (Tile *tile)
|
||||
{
|
||||
|
@ -247,6 +277,10 @@ tile_swap_init ()
|
|||
|
||||
swap_files = g_hash_table_new ((GHashFunc) tile_swap_hash,
|
||||
(GCompareFunc) tile_swap_compare);
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_create (&swapin_thread, NULL, &tile_swap_in_thread, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +384,9 @@ tile_swap_default (int fd,
|
|||
case SWAP_IN:
|
||||
tile_swap_default_in (def_swap_file, fd, tile);
|
||||
break;
|
||||
case SWAP_IN_ASYNC:
|
||||
tile_swap_default_in_async (def_swap_file, fd, tile);
|
||||
break;
|
||||
case SWAP_OUT:
|
||||
tile_swap_default_out (def_swap_file, fd, tile);
|
||||
break;
|
||||
|
@ -364,6 +401,51 @@ tile_swap_default (int fd,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
tile_swap_default_in_async (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile)
|
||||
{
|
||||
#ifdef HAVE_POSIX_THREADS
|
||||
AsyncSwapArgs *args;
|
||||
|
||||
args = g_new(AsyncSwapArgs, 1);
|
||||
args->def_swap_file = def_swap_file;
|
||||
args->fd = fd;
|
||||
args->tile = tile;
|
||||
|
||||
/* add this tile to the list of tiles for the async swapin task */
|
||||
pthread_mutex_lock (&async_swapin_mutex);
|
||||
g_slist_append (async_swapin_tiles, args);
|
||||
|
||||
if (async_swapin_tiles_end)
|
||||
async_swapin_tiles_end = async_swapin_tiles_end->next;
|
||||
else
|
||||
async_swapin_tiles_end = async_swapin_tiles;
|
||||
|
||||
pthread_cond_signal (&async_swapin_signal);
|
||||
pthread_mutex_unlock (&async_swapin_mutex);
|
||||
|
||||
#else
|
||||
/* ignore; it's only a hint anyway */
|
||||
/* this could be changed to call out to another program that
|
||||
* tries to make the OS read the data in from disk.
|
||||
*/
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOTE: if you change this function, check to see if your changes
|
||||
* apply to tile_swap_in_attempt() near the end of the file. The
|
||||
* difference is that this version makes guarantees about what it
|
||||
* provides, but tile_swap_in_attempt() just tries and gives up if
|
||||
* anything goes wrong.
|
||||
*
|
||||
* I'm not sure that it is worthwhile to try to pull out common
|
||||
* bits; I think the two functions are (at least for now) different
|
||||
* enough to keep as two functions.
|
||||
*/
|
||||
static void
|
||||
tile_swap_default_in (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
|
@ -376,6 +458,13 @@ tile_swap_default_in (DefSwapFile *def_swap_file,
|
|||
|
||||
err = -1;
|
||||
|
||||
TILE_MUTEX_LOCK (tile);
|
||||
if (tile->data)
|
||||
{
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (def_swap_file->cur_position != tile->swap_offset)
|
||||
{
|
||||
def_swap_file->cur_position = tile->swap_offset;
|
||||
|
@ -416,6 +505,9 @@ tile_swap_default_in (DefSwapFile *def_swap_file,
|
|||
/* Do not delete the swap from the file */
|
||||
/* tile_swap_default_delete (def_swap_file, fd, tile); */
|
||||
|
||||
/* FIXME: can this be moved upwards? */
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
|
||||
read_err_msg = seek_err_msg = TRUE;
|
||||
}
|
||||
|
||||
|
@ -468,8 +560,9 @@ tile_swap_default_out (DefSwapFile *def_swap_file,
|
|||
|
||||
def_swap_file->cur_position += rbytes;
|
||||
|
||||
/* g_free (tile->data);
|
||||
tile->data = NULL; */
|
||||
/* Do NOT free tile->data because we may be pre-swapping.
|
||||
* tile->data is freed in tile_cache_zorch_next
|
||||
*/
|
||||
tile->dirty = FALSE;
|
||||
|
||||
write_err_msg = seek_err_msg = TRUE;
|
||||
|
@ -652,3 +745,100 @@ tile_swap_gap_destroy (Gap *gap)
|
|||
{
|
||||
g_free (gap);
|
||||
}
|
||||
|
||||
|
||||
#if USE_PTHREADS
|
||||
/* go through the list of tiles that are likely to be used soon and
|
||||
* try to swap them in. If any tile is not in a state to be swapped
|
||||
* in, ignore it, and the error will get dealt with when the tile
|
||||
* is really needed -- assuming that the error still happens.
|
||||
*
|
||||
* Potential future enhancement: for non-threaded systems, we could
|
||||
* fork() a process which merely attempts to bring tiles into the
|
||||
* OS's buffer/page cache, where they will be read into the gimp
|
||||
* more quickly. This would be pretty trivial, actually.
|
||||
*/
|
||||
|
||||
static void
|
||||
tile_swap_in_attempt (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile)
|
||||
{
|
||||
int bytes;
|
||||
int err;
|
||||
int nleft;
|
||||
off_t offset;
|
||||
|
||||
err = -1;
|
||||
|
||||
TILE_MUTEX_LOCK (tile);
|
||||
if (tile->data)
|
||||
goto out;
|
||||
|
||||
if (!tile->swap_num || !tile->swap_offset)
|
||||
goto out;
|
||||
|
||||
if (def_swap_file->cur_position != tile->swap_offset)
|
||||
{
|
||||
def_swap_file->cur_position = tile->swap_offset;
|
||||
|
||||
offset = lseek (fd, tile->swap_offset, SEEK_SET);
|
||||
if (offset == -1)
|
||||
return;
|
||||
}
|
||||
|
||||
bytes = tile_size (tile);
|
||||
tile_alloc (tile);
|
||||
|
||||
nleft = bytes;
|
||||
while (nleft > 0)
|
||||
{
|
||||
do {
|
||||
err = read (fd, tile->data + bytes - nleft, nleft);
|
||||
} while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
g_free (tile->data);
|
||||
return;
|
||||
}
|
||||
|
||||
nleft -= err;
|
||||
}
|
||||
|
||||
def_swap_file->cur_position += bytes;
|
||||
|
||||
out:
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
}
|
||||
|
||||
static void *
|
||||
tile_swap_in_thread (void *data)
|
||||
{
|
||||
AsyncSwapArgs *args;
|
||||
GSList free_item;
|
||||
|
||||
while (1)
|
||||
{
|
||||
pthread_mutex_lock (&async_swapin_mutex);
|
||||
|
||||
if (!async_swapin_tiles)
|
||||
{
|
||||
pthread_cond_wait (&async_swapin_signal, &async_swapin_mutex);
|
||||
}
|
||||
args = async_swapin_tiles->data;
|
||||
|
||||
free_item = async_swapin_tiles;
|
||||
async_swapin_tiles = async_swapin_tiles->next;
|
||||
g_slist_free_1(free_item);
|
||||
if (!async_swapin_tiles)
|
||||
async_swapin_tiles_end = NULL;
|
||||
|
||||
pthread_mutex_unlock (&async_swapin_mutex);
|
||||
|
||||
tile_swap_in_attempt(args->def_swap_file, args->fd, args->tile);
|
||||
|
||||
g_free(args);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
typedef enum {
|
||||
SWAP_IN = 1,
|
||||
SWAP_IN_ASYNC,
|
||||
SWAP_OUT,
|
||||
SWAP_DELETE,
|
||||
SWAP_COMPRESS
|
||||
|
@ -24,6 +25,7 @@ int tile_swap_add (char *filename,
|
|||
gpointer user_data);
|
||||
void tile_swap_remove (int swap_num);
|
||||
void tile_swap_in (Tile *tile);
|
||||
void tile_swap_in_async (Tile *tile);
|
||||
void tile_swap_out (Tile *tile);
|
||||
void tile_swap_delete (Tile *tile);
|
||||
void tile_swap_compress (int swap_num);
|
||||
|
|
|
@ -104,6 +104,22 @@ pixel_region_resize (PR, x, y, w, h)
|
|||
PR->h = h;
|
||||
}
|
||||
|
||||
/* request that tiles within a region be fetched asynchronously
|
||||
*/
|
||||
void
|
||||
pixel_region_get_async (PR, ulx, uly, lrx, lry)
|
||||
PixelRegion *PR;
|
||||
int ulx;
|
||||
int uly;
|
||||
int lrx;
|
||||
int lry;
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for (y = uly; y < lry; y += TILE_HEIGHT)
|
||||
for (x = ulx; x < lrx; x += TILE_WIDTH)
|
||||
tile_manager_get_async (PR->tiles, x, y, 0);
|
||||
}
|
||||
|
||||
void
|
||||
pixel_region_get_row (PR, x, y, w, data, subsample)
|
||||
|
@ -122,6 +138,8 @@ pixel_region_get_row (PR, x, y, w, data, subsample)
|
|||
|
||||
end = x + w;
|
||||
|
||||
pixel_region_get_async (PR, x, y, end, y);
|
||||
|
||||
while (x < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, FALSE);
|
||||
|
@ -156,6 +174,8 @@ pixel_region_set_row (PR, x, y, w, data)
|
|||
|
||||
end = x + w;
|
||||
|
||||
pixel_region_get_async (PR, x, y, end, y);
|
||||
|
||||
while (x < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, TRUE);
|
||||
|
@ -190,6 +210,8 @@ pixel_region_get_col (PR, x, y, h, data, subsample)
|
|||
|
||||
end = y + h;
|
||||
|
||||
pixel_region_get_async (PR, x, y, x, end);
|
||||
|
||||
while (y < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, FALSE);
|
||||
|
@ -225,6 +247,8 @@ pixel_region_set_col (PR, x, y, h, data)
|
|||
|
||||
end = y + h;
|
||||
|
||||
pixel_region_get_async (PR, x, y, x, end);
|
||||
|
||||
while (y < end)
|
||||
{
|
||||
tile = tile_manager_get_tile (PR->tiles, x, y, 0, TRUE, TRUE);
|
||||
|
|
|
@ -38,6 +38,8 @@ struct _PixelRegion
|
|||
/* PixelRegion functions */
|
||||
void pixel_region_init (PixelRegion *, TileManager *, int, int, int, int, int);
|
||||
void pixel_region_resize (PixelRegion *, int, int, int, int);
|
||||
void pixel_region_get_async (PixelRegion *PR, int ulx, int uly,
|
||||
int lrx, int lry);
|
||||
void pixel_region_get_row (PixelRegion *, int, int, int, unsigned char *, int);
|
||||
void pixel_region_set_row (PixelRegion *, int, int, int, unsigned char *);
|
||||
void pixel_region_get_col (PixelRegion *, int, int, int, unsigned char *, int);
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
static void tile_manager_destroy_level (TileManager *tm,
|
||||
TileLevel *level);
|
||||
static void tile_invalidate (Tile **tile_ptr, TileManager *tm, int tile_num);
|
||||
static int tile_manager_get_tile_num (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level);
|
||||
|
||||
|
||||
TileManager*
|
||||
|
@ -164,23 +168,12 @@ tile_manager_get_tile (TileManager *tm,
|
|||
int wantread,
|
||||
int wantwrite)
|
||||
{
|
||||
TileLevel *tile_level;
|
||||
int tile_row;
|
||||
int tile_col;
|
||||
int tile_num;
|
||||
|
||||
if ((level < 0) || (level >= tm->nlevels))
|
||||
tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel, level);
|
||||
if (tile_num < 0)
|
||||
return NULL;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
if ((xpixel < 0) || (xpixel >= tile_level->width) ||
|
||||
(ypixel < 0) || (ypixel >= tile_level->height))
|
||||
return NULL;
|
||||
|
||||
tile_row = ypixel / TILE_HEIGHT;
|
||||
tile_col = xpixel / TILE_WIDTH;
|
||||
tile_num = tile_row * tile_level->ntile_cols + tile_col;
|
||||
|
||||
return tile_manager_get (tm, tile_num, level, wantread, wantwrite);
|
||||
}
|
||||
|
||||
|
@ -273,6 +266,26 @@ tile_manager_get (TileManager *tm,
|
|||
return *tile_ptr;
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_get_async (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level)
|
||||
{
|
||||
Tile *tile_ptr;
|
||||
TileLevel *tile_level;
|
||||
int tile_num;
|
||||
|
||||
tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel, level);
|
||||
if (tile_num < 0)
|
||||
return;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
tile_ptr = tile_level->tiles[tile_num];
|
||||
|
||||
tile_swap_in_async (tile_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
tile_manager_validate (TileManager *tm,
|
||||
Tile *tile)
|
||||
|
@ -597,3 +610,29 @@ tile_manager_map (TileManager *tm,
|
|||
|
||||
/* printf("}");fflush(stdout);*/
|
||||
}
|
||||
|
||||
static int
|
||||
tile_manager_get_tile_num (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level)
|
||||
{
|
||||
TileLevel *tile_level;
|
||||
int tile_row;
|
||||
int tile_col;
|
||||
int tile_num;
|
||||
|
||||
if ((level < 0) || (level >= tm->nlevels))
|
||||
return -1;
|
||||
|
||||
tile_level = &tm->levels[level];
|
||||
if ((xpixel < 0) || (xpixel >= tile_level->width) ||
|
||||
(ypixel < 0) || (ypixel >= tile_level->height))
|
||||
return -1;
|
||||
|
||||
tile_row = ypixel / TILE_HEIGHT;
|
||||
tile_col = xpixel / TILE_WIDTH;
|
||||
tile_num = tile_row * tile_level->ntile_cols + tile_col;
|
||||
|
||||
return tile_num;
|
||||
}
|
||||
|
|
|
@ -93,6 +93,16 @@ Tile* tile_manager_get (TileManager *tm,
|
|||
int wantread,
|
||||
int wantwrite);
|
||||
|
||||
/* Request that (if possible) the tile at x,y,layer be swapped
|
||||
* in. This is only a hint to improve performance; no guarantees.
|
||||
* The tile may be swapped in or otherwise made more accessible
|
||||
* if it is convenient...
|
||||
*/
|
||||
void tile_manager_get_async (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
int level);
|
||||
|
||||
void tile_manager_map_tile (TileManager *tm,
|
||||
int xpixel,
|
||||
int ypixel,
|
||||
|
|
200
app/tile_swap.c
200
app/tile_swap.c
|
@ -15,9 +15,10 @@
|
|||
|
||||
#include "tile_pvt.h" /* ick. */
|
||||
|
||||
typedef struct _SwapFile SwapFile;
|
||||
typedef struct _DefSwapFile DefSwapFile;
|
||||
typedef struct _Gap Gap;
|
||||
typedef struct _SwapFile SwapFile;
|
||||
typedef struct _DefSwapFile DefSwapFile;
|
||||
typedef struct _Gap Gap;
|
||||
typedef struct _AsyncSwapArgs AsyncSwapArgs;
|
||||
|
||||
struct _SwapFile
|
||||
{
|
||||
|
@ -41,6 +42,13 @@ struct _Gap
|
|||
long end;
|
||||
};
|
||||
|
||||
struct _AsyncSwapInfo
|
||||
{
|
||||
DefSwapFile *def_swap_file;
|
||||
int fd;
|
||||
Tile *tile;
|
||||
};
|
||||
|
||||
|
||||
static void tile_swap_init (void);
|
||||
static guint tile_swap_hash (int *key);
|
||||
|
@ -57,6 +65,9 @@ static int tile_swap_default (int fd,
|
|||
static void tile_swap_default_in (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
static void tile_swap_default_in_async (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
static void tile_swap_default_out (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile);
|
||||
|
@ -72,6 +83,9 @@ static void tile_swap_resize (DefSwapFile *def_swap_file,
|
|||
static Gap* tile_swap_gap_new (long start,
|
||||
long end);
|
||||
static void tile_swap_gap_destroy (Gap *gap);
|
||||
#ifdef USE_PTHREADS
|
||||
static void* tile_swap_in_thread (void *);
|
||||
#endif
|
||||
|
||||
|
||||
static int initialize = TRUE;
|
||||
|
@ -82,6 +96,13 @@ static int next_swap_num = 1;
|
|||
static long swap_file_grow = 16 * TILE_WIDTH * TILE_HEIGHT * 4;
|
||||
#ifdef USE_PTHREADS
|
||||
static pthread_mutex_t swapfile_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* async_swapin_mutex protects only the list, not the tiles therein */
|
||||
static pthread_t swapin_thread;
|
||||
static pthread_mutex_t async_swapin_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t async_swapin_signal = PTHREAD_COND_INITIALIZER;
|
||||
static GSList *async_swapin_tiles = NULL;
|
||||
static GSList *async_swapin_tiles_end = NULL;
|
||||
#endif
|
||||
|
||||
static gboolean seek_err_msg = TRUE, read_err_msg = TRUE, write_err_msg = TRUE;
|
||||
|
@ -207,6 +228,15 @@ out:
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
tile_swap_in_async (Tile *tile)
|
||||
{
|
||||
if (tile->swap_offset == -1)
|
||||
return;
|
||||
|
||||
tile_swap_command (tile, SWAP_IN_ASYNC);
|
||||
}
|
||||
|
||||
void
|
||||
tile_swap_in (Tile *tile)
|
||||
{
|
||||
|
@ -247,6 +277,10 @@ tile_swap_init ()
|
|||
|
||||
swap_files = g_hash_table_new ((GHashFunc) tile_swap_hash,
|
||||
(GCompareFunc) tile_swap_compare);
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
pthread_create (&swapin_thread, NULL, &tile_swap_in_thread, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +384,9 @@ tile_swap_default (int fd,
|
|||
case SWAP_IN:
|
||||
tile_swap_default_in (def_swap_file, fd, tile);
|
||||
break;
|
||||
case SWAP_IN_ASYNC:
|
||||
tile_swap_default_in_async (def_swap_file, fd, tile);
|
||||
break;
|
||||
case SWAP_OUT:
|
||||
tile_swap_default_out (def_swap_file, fd, tile);
|
||||
break;
|
||||
|
@ -364,6 +401,51 @@ tile_swap_default (int fd,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
tile_swap_default_in_async (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile)
|
||||
{
|
||||
#ifdef HAVE_POSIX_THREADS
|
||||
AsyncSwapArgs *args;
|
||||
|
||||
args = g_new(AsyncSwapArgs, 1);
|
||||
args->def_swap_file = def_swap_file;
|
||||
args->fd = fd;
|
||||
args->tile = tile;
|
||||
|
||||
/* add this tile to the list of tiles for the async swapin task */
|
||||
pthread_mutex_lock (&async_swapin_mutex);
|
||||
g_slist_append (async_swapin_tiles, args);
|
||||
|
||||
if (async_swapin_tiles_end)
|
||||
async_swapin_tiles_end = async_swapin_tiles_end->next;
|
||||
else
|
||||
async_swapin_tiles_end = async_swapin_tiles;
|
||||
|
||||
pthread_cond_signal (&async_swapin_signal);
|
||||
pthread_mutex_unlock (&async_swapin_mutex);
|
||||
|
||||
#else
|
||||
/* ignore; it's only a hint anyway */
|
||||
/* this could be changed to call out to another program that
|
||||
* tries to make the OS read the data in from disk.
|
||||
*/
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOTE: if you change this function, check to see if your changes
|
||||
* apply to tile_swap_in_attempt() near the end of the file. The
|
||||
* difference is that this version makes guarantees about what it
|
||||
* provides, but tile_swap_in_attempt() just tries and gives up if
|
||||
* anything goes wrong.
|
||||
*
|
||||
* I'm not sure that it is worthwhile to try to pull out common
|
||||
* bits; I think the two functions are (at least for now) different
|
||||
* enough to keep as two functions.
|
||||
*/
|
||||
static void
|
||||
tile_swap_default_in (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
|
@ -376,6 +458,13 @@ tile_swap_default_in (DefSwapFile *def_swap_file,
|
|||
|
||||
err = -1;
|
||||
|
||||
TILE_MUTEX_LOCK (tile);
|
||||
if (tile->data)
|
||||
{
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (def_swap_file->cur_position != tile->swap_offset)
|
||||
{
|
||||
def_swap_file->cur_position = tile->swap_offset;
|
||||
|
@ -416,6 +505,9 @@ tile_swap_default_in (DefSwapFile *def_swap_file,
|
|||
/* Do not delete the swap from the file */
|
||||
/* tile_swap_default_delete (def_swap_file, fd, tile); */
|
||||
|
||||
/* FIXME: can this be moved upwards? */
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
|
||||
read_err_msg = seek_err_msg = TRUE;
|
||||
}
|
||||
|
||||
|
@ -468,8 +560,9 @@ tile_swap_default_out (DefSwapFile *def_swap_file,
|
|||
|
||||
def_swap_file->cur_position += rbytes;
|
||||
|
||||
/* g_free (tile->data);
|
||||
tile->data = NULL; */
|
||||
/* Do NOT free tile->data because we may be pre-swapping.
|
||||
* tile->data is freed in tile_cache_zorch_next
|
||||
*/
|
||||
tile->dirty = FALSE;
|
||||
|
||||
write_err_msg = seek_err_msg = TRUE;
|
||||
|
@ -652,3 +745,100 @@ tile_swap_gap_destroy (Gap *gap)
|
|||
{
|
||||
g_free (gap);
|
||||
}
|
||||
|
||||
|
||||
#if USE_PTHREADS
|
||||
/* go through the list of tiles that are likely to be used soon and
|
||||
* try to swap them in. If any tile is not in a state to be swapped
|
||||
* in, ignore it, and the error will get dealt with when the tile
|
||||
* is really needed -- assuming that the error still happens.
|
||||
*
|
||||
* Potential future enhancement: for non-threaded systems, we could
|
||||
* fork() a process which merely attempts to bring tiles into the
|
||||
* OS's buffer/page cache, where they will be read into the gimp
|
||||
* more quickly. This would be pretty trivial, actually.
|
||||
*/
|
||||
|
||||
static void
|
||||
tile_swap_in_attempt (DefSwapFile *def_swap_file,
|
||||
int fd,
|
||||
Tile *tile)
|
||||
{
|
||||
int bytes;
|
||||
int err;
|
||||
int nleft;
|
||||
off_t offset;
|
||||
|
||||
err = -1;
|
||||
|
||||
TILE_MUTEX_LOCK (tile);
|
||||
if (tile->data)
|
||||
goto out;
|
||||
|
||||
if (!tile->swap_num || !tile->swap_offset)
|
||||
goto out;
|
||||
|
||||
if (def_swap_file->cur_position != tile->swap_offset)
|
||||
{
|
||||
def_swap_file->cur_position = tile->swap_offset;
|
||||
|
||||
offset = lseek (fd, tile->swap_offset, SEEK_SET);
|
||||
if (offset == -1)
|
||||
return;
|
||||
}
|
||||
|
||||
bytes = tile_size (tile);
|
||||
tile_alloc (tile);
|
||||
|
||||
nleft = bytes;
|
||||
while (nleft > 0)
|
||||
{
|
||||
do {
|
||||
err = read (fd, tile->data + bytes - nleft, nleft);
|
||||
} while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
|
||||
|
||||
if (err <= 0)
|
||||
{
|
||||
g_free (tile->data);
|
||||
return;
|
||||
}
|
||||
|
||||
nleft -= err;
|
||||
}
|
||||
|
||||
def_swap_file->cur_position += bytes;
|
||||
|
||||
out:
|
||||
TILE_MUTEX_UNLOCK (tile);
|
||||
}
|
||||
|
||||
static void *
|
||||
tile_swap_in_thread (void *data)
|
||||
{
|
||||
AsyncSwapArgs *args;
|
||||
GSList free_item;
|
||||
|
||||
while (1)
|
||||
{
|
||||
pthread_mutex_lock (&async_swapin_mutex);
|
||||
|
||||
if (!async_swapin_tiles)
|
||||
{
|
||||
pthread_cond_wait (&async_swapin_signal, &async_swapin_mutex);
|
||||
}
|
||||
args = async_swapin_tiles->data;
|
||||
|
||||
free_item = async_swapin_tiles;
|
||||
async_swapin_tiles = async_swapin_tiles->next;
|
||||
g_slist_free_1(free_item);
|
||||
if (!async_swapin_tiles)
|
||||
async_swapin_tiles_end = NULL;
|
||||
|
||||
pthread_mutex_unlock (&async_swapin_mutex);
|
||||
|
||||
tile_swap_in_attempt(args->def_swap_file, args->fd, args->tile);
|
||||
|
||||
g_free(args);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
typedef enum {
|
||||
SWAP_IN = 1,
|
||||
SWAP_IN_ASYNC,
|
||||
SWAP_OUT,
|
||||
SWAP_DELETE,
|
||||
SWAP_COMPRESS
|
||||
|
@ -24,6 +25,7 @@ int tile_swap_add (char *filename,
|
|||
gpointer user_data);
|
||||
void tile_swap_remove (int swap_num);
|
||||
void tile_swap_in (Tile *tile);
|
||||
void tile_swap_in_async (Tile *tile);
|
||||
void tile_swap_out (Tile *tile);
|
||||
void tile_swap_delete (Tile *tile);
|
||||
void tile_swap_compress (int swap_num);
|
||||
|
|
Loading…
Reference in New Issue