1998-07-03 07:29:44 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "tile.h"
|
1998-07-08 14:41:58 +08:00
|
|
|
#include "tile_pvt.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
#include "tile_cache.h"
|
|
|
|
#include "tile_manager.h"
|
|
|
|
#include "tile_swap.h"
|
|
|
|
|
1998-07-08 14:41:58 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
/* EXPERIMENTAL Copy-On-Write goodies
|
|
|
|
* by Adam D. Moss
|
|
|
|
* adam@gimp.org
|
|
|
|
* adam@foxbox.org
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* C.O.W. Revisions:
|
|
|
|
*
|
|
|
|
* 97.10.05 - Initial release
|
|
|
|
* 97.10.06 - Much faster tile invalidation +
|
|
|
|
* Better swap interaction (should no longer
|
|
|
|
* crash GIMP when GIMP swapfile is full).
|
|
|
|
* 97.10.18 - Very stable now, and even more efficient.
|
|
|
|
* 98.06.16 - Revised from GIMP 0.99.14 for 1.[01].0 - no
|
|
|
|
* longer so sure about stability until
|
|
|
|
* more comprehensive testing is done.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* MISC TODO:
|
|
|
|
*
|
|
|
|
* tile_invalidate: (tile_manager) - don't let a tile become
|
|
|
|
* invalidated if its ref-count >1, but move it to a delete-on-last-unref
|
|
|
|
* list instead...
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Define MUCH_TILE_DEBUG in addition to TILE_DEBUG to get
|
|
|
|
* debugging for every single tile_ref2 and tile_unref (that's
|
|
|
|
* a lot).
|
|
|
|
#define MUCH_TILE_DEBUG heckyeah
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
tile_init (Tile *tile,
|
|
|
|
int bpp)
|
|
|
|
{
|
|
|
|
tile->ref_count = 0;
|
|
|
|
tile->dirty = FALSE;
|
|
|
|
tile->valid = FALSE;
|
1998-07-03 07:29:44 +08:00
|
|
|
tile->data = NULL;
|
|
|
|
tile->real_tile_ptr = NULL;
|
|
|
|
tile->mirrored_by = NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
tile->ewidth = TILE_WIDTH;
|
|
|
|
tile->eheight = TILE_HEIGHT;
|
|
|
|
tile->bpp = bpp;
|
|
|
|
tile->tile_num = -1;
|
|
|
|
tile->swap_num = 1;
|
|
|
|
tile->swap_offset = -1;
|
|
|
|
tile->tm = NULL;
|
1998-06-21 01:09:32 +08:00
|
|
|
tile->next = tile->prev = NULL;
|
|
|
|
tile->listhead = NULL;
|
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
{
|
|
|
|
pthread_mutex_init(&tile->mutex, NULL);
|
|
|
|
}
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
int tile_ref_count = 0;
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
void
|
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__)
|
|
|
|
_tile_ref (Tile *tile, char *func_name)
|
|
|
|
#else
|
|
|
|
tile_ref (Tile *tile)
|
|
|
|
#endif
|
1998-07-03 07:29:44 +08:00
|
|
|
{
|
|
|
|
/* While things get moved over to the new tile_ref2
|
|
|
|
* interface, tile_ref is a wrapper which just says
|
|
|
|
* 'yes, we'll be dirtying the tile'
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__)
|
|
|
|
printf("COW-Warning: function %s is using obsolete tile_ref interface\n",
|
|
|
|
func_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__)
|
|
|
|
_tile_ref2 (tile, TRUE, func_name);
|
|
|
|
#else
|
|
|
|
tile_ref2 (tile, TRUE);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
tile_is_mirroring (Tile *tile)
|
|
|
|
{
|
|
|
|
return (tile->real_tile_ptr != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
tile_is_mirrored (Tile *tile)
|
|
|
|
{
|
|
|
|
return (tile->mirrored_by != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
tile_is_real (Tile *tile)
|
|
|
|
{
|
|
|
|
return (tile->real_tile_ptr == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Follow the real_tile_ptr links back to the tile which provides
|
|
|
|
* the real source data for the given tile
|
|
|
|
*/
|
|
|
|
Tile *
|
|
|
|
tile_find_nonmirroring (Tile *tile)
|
|
|
|
{
|
|
|
|
if (! tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return tile_find_nonmirroring (tile->real_tile_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
Tile *
|
|
|
|
tile_find_finalmirroring (Tile *tile)
|
|
|
|
{
|
|
|
|
if (! tile_is_mirrored (tile))
|
|
|
|
{
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return tile_find_finalmirroring (tile->mirrored_by);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Take a mirroring-tile and turn it into a bona fide self-contained
|
|
|
|
* tile.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tile_devirtualize (Tile *tile)
|
|
|
|
{
|
|
|
|
Tile *real_tile;
|
|
|
|
|
|
|
|
/* Sanity */
|
|
|
|
if (tile_is_real (tile))
|
|
|
|
g_error ("Tried to devirtualize a real tile");
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
if (tile->ref_count == 0)
|
|
|
|
g_warning ("Trying to devirtualize a mirroring-tile with no ref_count");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Go find the tile ('real_tile') which owns the real data
|
|
|
|
*/
|
|
|
|
real_tile = tile_find_nonmirroring (tile);
|
|
|
|
|
|
|
|
/* Sanity */
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
if (real_tile->ref_count == 0)
|
|
|
|
g_warning ("Trying to devirtualize a mirroring-tile whose real_tile has no ref_count");
|
|
|
|
#endif
|
|
|
|
if (!real_tile->valid)
|
|
|
|
g_warning ("Trying to devirtualize a mirroring-tile whose real_tile is !valid");
|
|
|
|
|
|
|
|
/* Copy the actual tile data from the real_tile to this tile
|
|
|
|
*/
|
|
|
|
tile->data = NULL;
|
|
|
|
tile_alloc (tile);
|
|
|
|
/* printf ("{ %dx%d : %d - %p[%p]->%p[%p] }", real_tile->ewidth, real_tile->eheight,
|
|
|
|
real_tile->bpp, real_tile, real_tile->data, tile, tile->data);
|
|
|
|
fflush(stdout);*/
|
|
|
|
|
|
|
|
memcpy (tile->data, real_tile->data, tile_size(real_tile));
|
|
|
|
|
|
|
|
/* 'tile' is now a real tile. */
|
|
|
|
tile->real_tile_ptr = NULL;
|
|
|
|
tile->valid = TRUE;
|
|
|
|
|
|
|
|
tile_cache_insert(tile);
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("Tile at %p is now devirtualized.\n", tile);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Make this tile self-contained.
|
|
|
|
*
|
|
|
|
* The next tile in the linked-list of tiles which are mirroring 'tile'
|
|
|
|
* is promoted to a real physical tile and unlinked from 'tile'. This
|
|
|
|
* renders 'tile' safe for dirtying (or destruction).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tile_isolate (Tile *tile)
|
|
|
|
{
|
|
|
|
Tile *temp_tileptr;
|
|
|
|
|
|
|
|
/* Sanity
|
|
|
|
*/
|
|
|
|
if (! (tile_is_mirrored (tile) || tile_is_mirroring (tile)))
|
|
|
|
{
|
|
|
|
g_warning ("Tried to isolate a tile which is neither a mirror source "
|
|
|
|
"nor destination");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This tile is both linked to and linked from? */
|
|
|
|
if (tile_is_mirrored (tile) && tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
temp_tileptr = tile->real_tile_ptr;
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was middle of chain - relinking %p and %p\n",
|
|
|
|
tile,
|
|
|
|
temp_tileptr,
|
|
|
|
tile->mirrored_by);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
tile->mirrored_by->real_tile_ptr = temp_tileptr;
|
|
|
|
temp_tileptr->mirrored_by = tile->mirrored_by;
|
|
|
|
|
|
|
|
tile_ref2 (temp_tileptr, FALSE);
|
|
|
|
tile_devirtualize (tile);
|
|
|
|
tile_unref (temp_tileptr, FALSE);
|
|
|
|
|
|
|
|
tile->mirrored_by = NULL;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This tile is mirroring another, but is not mirrored itself? */
|
|
|
|
if (tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
temp_tileptr = tile->real_tile_ptr;
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was end of chain - cauterizing %p\n",
|
|
|
|
tile,
|
|
|
|
temp_tileptr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* We stop mirroring the tile which we previously were -
|
|
|
|
* so reset that tile's mirrored_by pointer.
|
|
|
|
*/
|
|
|
|
temp_tileptr->mirrored_by = NULL;
|
|
|
|
|
|
|
|
tile_ref2 (temp_tileptr, FALSE);
|
|
|
|
tile_devirtualize (tile);
|
|
|
|
tile_unref (temp_tileptr, FALSE);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This tile is mirrored by another, but is not itself a mirror. */
|
|
|
|
if (tile_is_mirrored (tile))
|
|
|
|
{
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was source of chain - devirtualizing %p\n",
|
|
|
|
tile,
|
|
|
|
tile->mirrored_by);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
temp_tileptr = tile->mirrored_by;
|
|
|
|
|
|
|
|
tile_ref2 (temp_tileptr, FALSE);
|
|
|
|
tile_devirtualize (temp_tileptr);
|
|
|
|
tile_unref (temp_tileptr, FALSE);
|
|
|
|
|
|
|
|
/* The tile which was dependant on this one no longer is -
|
|
|
|
* so we can unref once.
|
|
|
|
*/
|
|
|
|
tile_unref (tile, FALSE);
|
|
|
|
|
|
|
|
tile->mirrored_by = NULL;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Turns dest_tile into a mirroring-tile which mirrors the given
|
|
|
|
* src_tile using copy-on-write.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tile_mirror (Tile *dest_tile, Tile *src_tile)
|
|
|
|
{
|
|
|
|
Tile *finalmirroring;
|
|
|
|
|
|
|
|
if (dest_tile == src_tile)
|
|
|
|
{
|
|
|
|
g_warning ("TRIED TO MIRROR TILE TO ITSELF");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("mirroring ");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (tile_is_real (dest_tile))
|
|
|
|
{
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("TO REAL ");
|
|
|
|
#endif
|
|
|
|
tile_invalidate (dest_tile);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tile_invalidate (dest_tile);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dest_tile->ref_count = 0; */
|
|
|
|
dest_tile->dirty = FALSE;
|
|
|
|
dest_tile->valid = FALSE;
|
|
|
|
dest_tile->data = NULL;
|
|
|
|
dest_tile->ewidth = src_tile->ewidth;
|
|
|
|
dest_tile->eheight = src_tile->eheight;
|
|
|
|
dest_tile->bpp = src_tile->bpp;
|
|
|
|
dest_tile->tile_num = -1; /* ! */
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
finalmirroring = tile_find_finalmirroring (src_tile);
|
|
|
|
dest_tile->real_tile_ptr = finalmirroring;
|
|
|
|
finalmirroring->mirrored_by = dest_tile;
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("%p -> %p\n", finalmirroring, dest_tile);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The following should be irrelevant in a mirroring tile - mirroring
|
|
|
|
* tiles by definition don't have real data of their own, so they can't
|
|
|
|
* be swapped. They don't have associated TileManagers either, since they
|
|
|
|
* rely on their mirrored source tile to contain validated data.
|
|
|
|
*/
|
|
|
|
dest_tile->swap_num = 1;
|
|
|
|
dest_tile->swap_offset = -1;
|
|
|
|
dest_tile->tm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__)
|
|
|
|
_tile_ref2 (Tile *tile, int dirty, char *func_name)
|
|
|
|
#else
|
|
|
|
tile_ref2 (Tile *tile, int dirty)
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1998-06-21 01:09:32 +08:00
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
pthread_mutex_lock(&(tile->mutex));
|
|
|
|
#endif
|
1998-07-03 07:29:44 +08:00
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__) && defined (MUCH_TILE_DEBUG)
|
|
|
|
g_print ("tile_ref2: %02d %c %p %s\n", tile->ref_count,
|
|
|
|
dirty?'d':' ',
|
|
|
|
tile,
|
|
|
|
func_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*g_print ("tile_ref2: %02d %c %p\n", tile->ref_count,
|
|
|
|
dirty?'d':' ',
|
|
|
|
tile);*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* Increment the global reference count.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
tile_ref_count += 1;
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
/* Increment this tile's reference count.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
|
|
|
tile->ref_count += 1;
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
if (tile_is_mirrored (tile) && dirty)
|
|
|
|
{
|
|
|
|
g_print ("Dirtying a mirrored tile: %p.\n", tile);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
if (dirty && tile->dirty)
|
|
|
|
{
|
|
|
|
g_print ("Not good: Dirtying a write-locked tile: %p.\n", tile);
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
|
|
/* if this is a read-only attachment to a mirroring tile,
|
|
|
|
* then ref the chain, update the data pointer, and return.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
if ((!dirty) && (tile_is_mirroring (tile)))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1998-07-03 07:29:44 +08:00
|
|
|
/* ref each of the tiles in the chain, back to the
|
|
|
|
* 'real' tile which sits at the start.
|
|
|
|
*/
|
|
|
|
#if USE_PTHREADS
|
|
|
|
pthread_mutex_unlock(&(tile->mutex));
|
|
|
|
#endif
|
|
|
|
tile_ref2 (tile->real_tile_ptr, FALSE);
|
|
|
|
tile->data = tile->real_tile_ptr->data;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* dirty, or clean-and-real */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
|
|
|
/* Real tile - first reference. */
|
|
|
|
if (!tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
/* If this is the first reference to the tile then
|
|
|
|
* swap the tile data in from disk. Note: this will
|
|
|
|
* properly handle the case where the tile data isn't
|
|
|
|
* on disk.
|
|
|
|
*/
|
|
|
|
if (tile->ref_count == 1)
|
|
|
|
{
|
|
|
|
tile_swap_in (tile);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Insert the tile into the cache. If the tile is already
|
|
|
|
* in the cache this will have the affect of "touching"
|
|
|
|
* the tile.
|
|
|
|
*/
|
|
|
|
tile_cache_insert (tile);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
1998-07-07 02:20:28 +08:00
|
|
|
#if USE_PTHREADS
|
|
|
|
pthread_mutex_unlock(&(tile->mutex));
|
|
|
|
#endif
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
/* Read/write attachment to a mirrored/ing tile - must be
|
|
|
|
* thoughtful.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
if (dirty)
|
|
|
|
{
|
|
|
|
/* Doing a read/write reference to a mirroring/ed tile -
|
|
|
|
* we'll have to turn the tile into a 'real' tile.
|
|
|
|
* Then return - we're done.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
if (tile_is_mirroring (tile) | tile_is_mirrored (tile))
|
|
|
|
{
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("r/w to mir'd/ing - isolating: ");
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
tile_isolate (tile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark the tile as dirty if it's being ref'd as dirtyable.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
tile->dirty |= dirty;
|
1998-06-28 13:34:46 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
/* Call 'tile_manager_validate' if the tile was invalid.
|
|
|
|
*/
|
1998-06-28 13:34:46 +08:00
|
|
|
if (!tile->valid)
|
|
|
|
tile_manager_validate ((TileManager*) tile->tm, tile);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
void
|
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__)
|
|
|
|
_tile_unref (Tile *tile, int dirty, char *func_name)
|
|
|
|
#else
|
|
|
|
tile_unref (Tile *tile, int dirty)
|
|
|
|
#endif
|
|
|
|
{
|
1998-06-21 01:09:32 +08:00
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
pthread_mutex_lock(&(tile->mutex));
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
#if defined (TILE_DEBUG) && defined (__GNUC__) && defined (MUCH_TILE_DEBUG)
|
|
|
|
g_print ("tile_unref: %02d %c %p %s\n", tile->ref_count,
|
|
|
|
dirty?'d':' ',
|
|
|
|
tile, func_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* g_print ("tile_unref: %02d %c %p\n", tile->ref_count,
|
|
|
|
dirty?'d':' ',
|
|
|
|
tile);*/
|
|
|
|
|
|
|
|
/* Decrement the global reference count.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
tile_ref_count -= 1;
|
|
|
|
|
1998-07-03 07:29:44 +08:00
|
|
|
/* Decrement this tile's reference count.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
|
|
|
tile->ref_count -= 1;
|
|
|
|
|
|
|
|
/* Mark the tile dirty if indicated
|
1998-07-03 07:29:44 +08:00
|
|
|
*
|
|
|
|
* commented out - we now dirty on ref, not unref
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
/*
|
|
|
|
tile->dirty |= dirty;
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
if (tile_is_mirroring (tile))
|
1998-06-21 01:09:32 +08:00
|
|
|
{
|
1998-07-03 07:29:44 +08:00
|
|
|
/* Mirroring tiles aren't allowed to be submitted as dirty -
|
|
|
|
* they should have been isolated at ref time.
|
|
|
|
*/
|
|
|
|
if (dirty)
|
|
|
|
g_warning ("Mirroring tile unref'd as dirty.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tile_is_mirrored (tile))
|
|
|
|
{
|
|
|
|
/* Mirrored tiles aren't allowed to be submitted as dirty -
|
|
|
|
* they should have been isolated at ref time.
|
|
|
|
*/
|
|
|
|
fflush(stdout);
|
|
|
|
if (dirty)
|
|
|
|
g_warning ("Mirrored tile unref'd as dirty.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* When we unref a mirroring tile, also unref the tile which
|
|
|
|
* was being mirrored.
|
|
|
|
*/
|
|
|
|
if (tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
/* Mirroring tiles aren't allowed to be submitted as dirty -
|
|
|
|
* they should have been ref'd as dirty in the first place so we
|
|
|
|
* could turn them into 'real' tiles.
|
|
|
|
*/
|
|
|
|
if (dirty)
|
|
|
|
{
|
|
|
|
g_warning ("Bleh, tried to unref a mirroring tile as dirty.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go find the mirrored tile and unref that too. */
|
|
|
|
#if USE_PTHREADS
|
|
|
|
pthread_mutex_unlock(&(tile->mutex));
|
|
|
|
#endif
|
|
|
|
tile_unref (tile->real_tile_ptr, FALSE);
|
|
|
|
|
|
|
|
return;
|
1998-06-21 01:09:32 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* If this was the last reference to the tile, then
|
|
|
|
* swap it out to disk.
|
|
|
|
*/
|
|
|
|
if (tile->ref_count == 0)
|
|
|
|
{
|
|
|
|
/* Only need to swap out in two cases:
|
1998-07-03 07:29:44 +08:00
|
|
|
* 1) The tile is dirty }
|
|
|
|
* 2) The tile has never been swapped } and is not mirroring
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
if ((tile->dirty || tile->swap_offset == -1)
|
|
|
|
&& !tile_is_mirroring (tile))
|
1997-11-25 06:05:25 +08:00
|
|
|
tile_swap_out (tile);
|
|
|
|
/* Otherwise, just throw out the data--the same stuff is in swap
|
|
|
|
*/
|
1998-07-03 07:29:44 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (! tile_is_mirroring (tile))
|
|
|
|
g_free (tile->data);
|
|
|
|
|
|
|
|
tile->data = NULL;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1998-06-21 01:09:32 +08:00
|
|
|
#if USE_PTHREADS
|
|
|
|
pthread_mutex_unlock(&(tile->mutex));
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tile_alloc (Tile *tile)
|
|
|
|
{
|
|
|
|
if (tile->data)
|
1998-06-21 01:09:32 +08:00
|
|
|
goto out;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* Allocate the data for the tile.
|
|
|
|
*/
|
|
|
|
tile->data = g_new (guchar, tile_size (tile));
|
1998-06-21 01:09:32 +08:00
|
|
|
out:
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
tile_size (Tile *tile)
|
|
|
|
{
|
1998-06-21 01:09:32 +08:00
|
|
|
int size;
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Return the actual size of the tile data.
|
|
|
|
* (Based on its effective width and height).
|
|
|
|
*/
|
1998-06-21 01:09:32 +08:00
|
|
|
size = tile->ewidth * tile->eheight * tile->bpp;
|
|
|
|
return size;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tile_invalidate (Tile *tile)
|
|
|
|
{
|
1998-06-21 01:09:32 +08:00
|
|
|
#ifdef USE_PTHREADS
|
|
|
|
pthread_mutex_lock(&(tile->mutex));
|
|
|
|
#endif
|
1998-07-03 07:29:44 +08:00
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
if (tile->ref_count > 1)
|
|
|
|
{
|
|
|
|
g_print (" (inv%p:ref%d) ", tile, tile->ref_count);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_print (" (inv%p) ", tile);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* If this tile is mirrored/ing, then maybe isolate it before we
|
|
|
|
* invalidate it, so that we don't accidentally delete a tile
|
|
|
|
* whose data is still in use by a mirror.
|
|
|
|
*/
|
|
|
|
if (tile_is_mirrored (tile))
|
|
|
|
{
|
|
|
|
if (tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
/* tile is in the middle of a chain. just relink its
|
|
|
|
* successor and predecessor. that's all we need to do for
|
|
|
|
* a cleanup.
|
|
|
|
*/
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was middle of chain - relinking %p and %p, "
|
|
|
|
"no isolation\n",
|
|
|
|
tile,
|
|
|
|
tile->real_tile_ptr,
|
|
|
|
tile->mirrored_by);
|
|
|
|
#endif
|
|
|
|
tile->mirrored_by->real_tile_ptr = tile->real_tile_ptr;
|
|
|
|
tile->real_tile_ptr->mirrored_by = tile->mirrored_by;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* tile is real, and mirrored. Copy its vital statistics to
|
|
|
|
* its successor in the tile chain, so it can be safely deleted.
|
|
|
|
*/
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was source of chain - successor %p swallows soul"
|
|
|
|
", no isolation\n",
|
|
|
|
tile,
|
|
|
|
tile->mirrored_by);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* remove 'tile' from cache - but keep the ref_count up
|
|
|
|
* so that the tile_unref() which tile_cache_flush() calls
|
|
|
|
* won't invalidate the tile (we'll be doing that ourselves).
|
|
|
|
*/
|
|
|
|
tile->ref_count++;
|
|
|
|
tile_cache_flush (tile);
|
|
|
|
tile->ref_count--;
|
|
|
|
|
|
|
|
/* imbue our successor with our data pointer, validity,
|
|
|
|
* tile manager, swap_num, swap_offset, and dirty
|
|
|
|
* flag
|
|
|
|
*/
|
|
|
|
tile->mirrored_by->data = tile->data;
|
|
|
|
tile->data = NULL;
|
|
|
|
|
|
|
|
tile->mirrored_by->dirty = tile->dirty;
|
|
|
|
tile->dirty = FALSE;
|
|
|
|
tile->mirrored_by->valid = tile->valid;
|
|
|
|
tile->valid = FALSE;
|
|
|
|
tile->mirrored_by->swap_num = tile->swap_num;
|
|
|
|
tile->swap_num = 0;
|
|
|
|
tile->mirrored_by->swap_offset = tile->swap_offset;
|
|
|
|
tile->swap_num = -1;
|
|
|
|
tile->mirrored_by->tm = tile->tm;
|
|
|
|
tile->tm = NULL;
|
|
|
|
|
|
|
|
/* sever links with our successor in both directions.
|
|
|
|
* our successor is now the new chain source.
|
|
|
|
*
|
|
|
|
* also register this newly-born 'real' tile with the tile cache.
|
|
|
|
*/
|
|
|
|
tile->mirrored_by->real_tile_ptr = NULL;
|
|
|
|
tile_cache_insert (tile->mirrored_by);
|
|
|
|
tile->mirrored_by = NULL;
|
|
|
|
|
|
|
|
/* This tile is as clean and invalid as it's going to get.
|
|
|
|
* Return.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* not mirrored, maybe mirroring */
|
|
|
|
{
|
|
|
|
/* for a non-real tile at the end of a chain, the only cleanup
|
|
|
|
* we have to do for its safe destruction is cauterize the
|
|
|
|
* flapping mirrored_by pointer of its predecessor on the chain.
|
|
|
|
*/
|
|
|
|
if (tile_is_mirroring (tile))
|
|
|
|
{
|
|
|
|
#if defined (TILE_DEBUG)
|
|
|
|
g_print ("tile %p: was end of chain - cauterizing %p, no "
|
|
|
|
"isolation\n",
|
|
|
|
tile,
|
|
|
|
tile->real_tile_ptr);
|
|
|
|
#endif
|
|
|
|
tile->real_tile_ptr->mirrored_by = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this isn't a 'real' tile then it doesn't need invalidating,
|
|
|
|
* since it doesn't have any unique data associated with it.
|
|
|
|
*/
|
|
|
|
if (!tile_is_real (tile))
|
|
|
|
{
|
|
|
|
if (tile->valid)
|
|
|
|
{
|
|
|
|
g_warning ("tried to invalidate a mirroring tile which was valid.");
|
|
|
|
tile->valid = FALSE;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Only 'real' tiles permitted past this point.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Invalidate the tile. (Must be valid first).
|
|
|
|
*/
|
|
|
|
if (tile->valid)
|
|
|
|
{
|
|
|
|
tile->valid = FALSE;
|
|
|
|
|
|
|
|
if (tile->data)
|
|
|
|
{
|
|
|
|
/* If the tile is in memory then trick the
|
|
|
|
* tile cache routines. We temporarily increment
|
|
|
|
* the reference count so that flushing a tile does
|
|
|
|
* not cause it to go out to disk.
|
|
|
|
*/
|
|
|
|
tile->ref_count += 1;
|
|
|
|
tile_cache_flush (tile);
|
|
|
|
tile->ref_count -= 1;
|
|
|
|
|
|
|
|
/* We then free the tile data.
|
|
|
|
*/
|
|
|
|
g_free (tile->data);
|
|
|
|
tile->data = NULL;
|
|
|
|
}
|
|
|
|
if (tile->swap_offset != -1)
|
|
|
|
{
|
|
|
|
/* If the tile is on disk, then delete its
|
|
|
|
* presence there.
|
|
|
|
*/
|
|
|
|
tile_swap_delete (tile);
|
|
|
|
}
|
|
|
|
}
|
1998-06-21 01:09:32 +08:00
|
|
|
#if USE_PTHREADS
|
|
|
|
pthread_mutex_unlock(&(tile->mutex));
|
|
|
|
#endif
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1998-07-03 07:29:44 +08:00
|
|
|
|
|
|
|
|