Commit d2e6afe0 authored by EDT 1998 Michael K. Johnson's avatar EDT 1998 Michael K. Johnson Committed by Michael Johnson
Browse files

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.
parent a178be49
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,