Commit 27e90260 authored by scott's avatar scott
Browse files

Incorporated Adam's copy-on-write patches. Seems to not break anything,


Incorporated Adam's copy-on-write patches.  Seems to not break
anything, but I'm sure they do somewhere. --sg
parent ddc557c2
Thu Jul 2 18:10:43 1998 Scott Goehring <scott@poverty.bloomington.in.us>
* blend.c boundary.c by_color_select.c channel.c color_picker.c
drawable_cmds.c frac.c fuzzy_select.c image_render.c invert.c
layer.c paint_core.c paint_funcs.c pixel_region.c plug_in.c tile.c
tile.h tile_cache.c tile_manager.c transform_core.c undo.c xcf.c:
incorporated Adam's copy-on-write patches. Tested briefly:
nothing seems to be broken, but I guarantee nothing.
Thu Jul 2 11:03:12 PDT 1998 Manish Singh <yosh@gimp.org>
* updated blur, randomize, and exchange plugins (not sure if
......@@ -358,7 +367,6 @@ Sat Jun 20 11:36:16 PDT 1998 Manish Singh <yosh@gimp.org>
* configure.in
* app/Makefile.am: really add thread support
>>>>>>> 1.454
Sat Jun 20 12:04:41 1998 Scott Goehring <scott@poverty.bloomington.in.us>
* app/tile.h: #include config.h so we can tell if we're doing
......@@ -393,7 +401,6 @@ Fri Jun 19 12:34:29 PDT 1998 Manish Singh <yosh@gimp.org>
* changes libgimp versioning to gtk-style. Bunch o' Makefile.am
changes for that
>>>>>>> 1.430
Thu Jun 18 23:11:36 1998 Owen Taylor <otaylor@gtk.org>
* app/ink.c: Shift the range to smaller brushes.
......
......@@ -1360,7 +1360,7 @@ plug_in_handle_tile_req (GPTileReq *tile_req)
return;
}
tile_ref (tile);
tile_ref2 (tile, TRUE);
if (tile_data.use_shm)
memcpy (tile->data, shm_addr, tile_size (tile));
......@@ -1407,7 +1407,7 @@ plug_in_handle_tile_req (GPTileReq *tile_req)
tile_data.height = tile->eheight;
tile_data.use_shm = (shm_ID == -1) ? FALSE : TRUE;
tile_ref (tile);
tile_ref2 (tile, FALSE);
if (tile_data.use_shm)
memcpy (shm_addr, tile->data, tile_size (tile));
......
......@@ -124,7 +124,7 @@ find_empty_segs (PixelRegion *maskPR,
if (tile)
tile_unref (tile, FALSE);
tile = tile_manager_get_tile (maskPR->tiles, x, scanline, 0);
tile_ref (tile);
tile_ref2 (tile, FALSE);
data = tile->data + tile->bpp *
((scanline % TILE_HEIGHT) * tile->ewidth + (x % TILE_WIDTH)) + (tile->bpp - 1);
tilex = x / TILE_WIDTH;
......
......@@ -124,7 +124,7 @@ pixel_region_get_row (PR, x, y, w, data, subsample)
while (x < end)
{
tile = tile_manager_get_tile (PR->tiles, x, y, 0);
tile_ref (tile);
tile_ref2 (tile, FALSE);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = x + (tile->ewidth - (x % TILE_WIDTH));
inc = subsample * tile->bpp;
......@@ -159,7 +159,7 @@ pixel_region_set_row (PR, x, y, w, data)
while (x < end)
{
tile = tile_manager_get_tile (PR->tiles, x, y, 0);
tile_ref (tile);
tile_ref2 (tile, TRUE);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = x + (tile->ewidth - (x % TILE_WIDTH));
......@@ -194,7 +194,7 @@ pixel_region_get_col (PR, x, y, h, data, subsample)
while (y < end)
{
tile = tile_manager_get_tile (PR->tiles, x, y, 0);
tile_ref (tile);
tile_ref2 (tile, FALSE);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = y + (tile->eheight - (y % TILE_HEIGHT));
inc = subsample * tile->bpp * tile->ewidth;
......@@ -230,7 +230,7 @@ pixel_region_set_col (PR, x, y, h, data)
while (y < end)
{
tile = tile_manager_get_tile (PR->tiles, x, y, 0);
tile_ref (tile);
tile_ref2 (tile, TRUE);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = y + (tile->eheight - (y % TILE_HEIGHT));
inc = tile->bpp * tile->ewidth;
......@@ -550,7 +550,7 @@ pixel_region_configure (PRH, PRI)
int offx, offy;
tile = tile_manager_get_tile (PRH->PR->tiles, PRH->PR->x, PRH->PR->y, 0);
tile_ref (tile);
tile_ref2 (tile, PRH->PR->dirty);
offx = PRH->PR->x % TILE_WIDTH;
offy = PRH->PR->y % TILE_HEIGHT;
......
#include <gtk/gtkmain.h>
#include <glib.h>
#include "gimprc.h"
#include "tile.h"
#include "tile_cache.h"
#include "tile_swap.h"
#ifdef USE_PTHREADS
......
......@@ -339,8 +339,8 @@ tile_manager_update_tile (TileManager *tm,
tile = tile_manager_get (tm, num, level);
tile_ref (tile);
tile_ref (toplevel_tile);
tile_ref2 (tile, TRUE);
tile_ref2 (toplevel_tile, FALSE);
tilew += tilex;
tileh += tiley;
......
#include <stdio.h>
#include "tile.h"
#include "tile_cache.h"
#include "tile_manager.h"
#include "tile_swap.h"
/* 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
*/
void
tile_init (Tile *tile,
......@@ -11,11 +46,13 @@ tile_init (Tile *tile,
tile->ref_count = 0;
tile->dirty = FALSE;
tile->valid = FALSE;
tile->data = NULL;
tile->real_tile_ptr = NULL;
tile->mirrored_by = NULL;
tile->ewidth = TILE_WIDTH;
tile->eheight = TILE_HEIGHT;
tile->bpp = bpp;
tile->tile_num = -1;
tile->data = NULL;
tile->swap_num = 1;
tile->swap_offset = -1;
tile->tm = NULL;
......@@ -28,57 +65,417 @@ tile_init (Tile *tile,
#endif
}
int tile_ref_count = 0;
void
#if defined (TILE_DEBUG) && defined (__GNUC__)
_tile_ref (Tile *tile, char *func_name)
#else
tile_ref (Tile *tile)
#endif
{
/* 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
{
#ifdef USE_PTHREADS
pthread_mutex_lock(&(tile->mutex));
#endif
/* g_print ("tile_ref: 0x%08x %s\n", tile, func_name); */
#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);*/
/* Increment the global reference count.
*/
tile_ref_count += 1;
/* Increment the reference count.
/* Increment this tile's reference count.
*/
tile->ref_count += 1;
/* 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 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.
*/
if (tile->ref_count == 1)
if ((!dirty) && (tile_is_mirroring (tile)))
{
tile_swap_in (tile);
/* 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 */
/* the tile must be clean */
tile->dirty = FALSE;
/* 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);
}
/* Insert the tile into the cache. If the tile is already
* in the cache this will have the affect of "touching"
* the tile.
/* Read/write attachment to a mirrored/ing tile - must be
* thoughtful.
*/
tile_cache_insert (tile);
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
/* Call 'tile_manager_validate' if the tile was invalid.
tile_isolate (tile);
}
}
}
/* Mark the tile as dirty if it's being ref'd as dirtyable.
*/
tile->dirty |= dirty;
#if USE_PTHREADS
pthread_mutex_unlock(&(tile->mutex));
#endif
/* Call 'tile_manager_validate' if the tile was invalid.
*/
if (!tile->valid)
tile_manager_validate ((TileManager*) tile->tm, tile);
}
void
#if defined (TILE_DEBUG) && defined (__GNUC__)
_tile_unref (Tile *tile, int dirty, char *func_name)
......@@ -89,20 +486,76 @@ tile_unref (Tile *tile, int dirty)
#ifdef USE_PTHREADS
pthread_mutex_lock(&(tile->mutex));
#endif
/* g_print ("tile_unref: 0x%08x %s\n", tile, func_name); */
#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.
*/
tile_ref_count -= 1;
/* Decrement the reference count.
/* Decrement this tile's reference count.
*/
tile->ref_count -= 1;
/* Mark the tile dirty if indicated
*
* commented out - we now dirty on ref, not unref