fix a race in im_render() invalidation
This commit is contained in:
parent
223c964496
commit
87fddf9cd1
@ -35,6 +35,7 @@
|
|||||||
- default to max number of processors (--vips-concurrency and IM_CONCURRENCY
|
- default to max number of processors (--vips-concurrency and IM_CONCURRENCY
|
||||||
set >0 can override) on linux and win32
|
set >0 can override) on linux and win32
|
||||||
- better nprocs guesser
|
- better nprocs guesser
|
||||||
|
- im_render() fixes to help the paintbox, some speedups too
|
||||||
|
|
||||||
15/1/10 started 7.21.1
|
15/1/10 started 7.21.1
|
||||||
- added "written" callbacks, used to implement write to non-vips formats
|
- added "written" callbacks, used to implement write to non-vips formats
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
* - drawing the mask image no longer sets those parts of the image
|
* - drawing the mask image no longer sets those parts of the image
|
||||||
* rendering, it just queries the cache
|
* rendering, it just queries the cache
|
||||||
* - better mask painting
|
* - better mask painting
|
||||||
|
* 17/3/10
|
||||||
|
* - don't use invalidate callbacks after all, just test region->invalid,
|
||||||
|
* much simpler!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -157,13 +160,6 @@ typedef struct {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Invalidate proxy. The im_invalidate() callback comes here, render can null
|
|
||||||
* out the pointer when it's not interested.
|
|
||||||
*/
|
|
||||||
typedef struct _RenderProxy {
|
|
||||||
struct _Render *render;
|
|
||||||
} RenderProxy;
|
|
||||||
|
|
||||||
/* Per-call state.
|
/* Per-call state.
|
||||||
*/
|
*/
|
||||||
typedef struct _Render {
|
typedef struct _Render {
|
||||||
@ -185,10 +181,6 @@ typedef struct _Render {
|
|||||||
notify_fn notify; /* Tell caller about paints here */
|
notify_fn notify; /* Tell caller about paints here */
|
||||||
void *client;
|
void *client;
|
||||||
|
|
||||||
/* The invalidate proxy: NULL this out to stop the callback.
|
|
||||||
*/
|
|
||||||
RenderProxy *proxy;
|
|
||||||
|
|
||||||
/* Make readers single thread with this. No point allowing
|
/* Make readers single thread with this. No point allowing
|
||||||
* multi-thread read.
|
* multi-thread read.
|
||||||
*/
|
*/
|
||||||
@ -267,10 +259,6 @@ render_free( Render *render )
|
|||||||
|
|
||||||
g_assert( render->ref_count == 0 );
|
g_assert( render->ref_count == 0 );
|
||||||
|
|
||||||
/* Block the invalidate callback.
|
|
||||||
*/
|
|
||||||
render->proxy->render = NULL;
|
|
||||||
|
|
||||||
render_dirty_remove( render );
|
render_dirty_remove( render );
|
||||||
|
|
||||||
IM_FREEF( im_threadgroup_free, render->tg );
|
IM_FREEF( im_threadgroup_free, render->tg );
|
||||||
@ -343,6 +331,8 @@ render_dirty_get( void )
|
|||||||
if( render_dirty_all ) {
|
if( render_dirty_all ) {
|
||||||
render = (Render *) render_dirty_all->data;
|
render = (Render *) render_dirty_all->data;
|
||||||
|
|
||||||
|
g_assert( render->ref_count == 1 );
|
||||||
|
|
||||||
/* Ref the render to make sure it can't die while we're
|
/* Ref the render to make sure it can't die while we're
|
||||||
* working on it.
|
* working on it.
|
||||||
*/
|
*/
|
||||||
@ -498,6 +488,9 @@ render_thread_main( void *client )
|
|||||||
|
|
||||||
render_dirty_put( render );
|
render_dirty_put( render );
|
||||||
|
|
||||||
|
g_assert( render->ref_count == 1 ||
|
||||||
|
render->ref_count == 2 );
|
||||||
|
|
||||||
/* _get() does a ref to make sure we keep the render
|
/* _get() does a ref to make sure we keep the render
|
||||||
* alive during processing ... unref before we loop.
|
* alive during processing ... unref before we loop.
|
||||||
* This can kill off the render.
|
* This can kill off the render.
|
||||||
@ -550,34 +543,6 @@ render_thread_create( void )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
static char *
|
|
||||||
tile_state_name( TileState state )
|
|
||||||
{
|
|
||||||
switch( state ) {
|
|
||||||
case TILE_DIRTY: return( "TILE_DIRTY" );
|
|
||||||
case TILE_WORKING: return( "TILE_WORKING" );
|
|
||||||
case TILE_PAINTED_FADING: return( "TILE_PAINTED_FADING" );
|
|
||||||
case TILE_PAINTED: return( "TILE_PAINTED" );
|
|
||||||
|
|
||||||
default:
|
|
||||||
g_assert( FALSE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
tile_print( Tile *tile )
|
|
||||||
{
|
|
||||||
printf( "tile: %dx%d, access_ticks = %d, time = %d,\n"
|
|
||||||
"\tstate = %s\n",
|
|
||||||
tile->area.left, tile->area.top,
|
|
||||||
tile->access_ticks, tile->time,
|
|
||||||
tile_state_name( tile->state ) );
|
|
||||||
|
|
||||||
return( NULL );
|
|
||||||
}
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
tile_test_clean_ticks( Tile *this, Tile **best )
|
tile_test_clean_ticks( Tile *this, Tile **best )
|
||||||
{
|
{
|
||||||
@ -616,42 +581,6 @@ render_tile_get_painted( Render *render )
|
|||||||
return( tile );
|
return( tile );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free all painted tiles. This is triggered on "invalidate".
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
render_invalidate( RenderProxy *proxy )
|
|
||||||
{
|
|
||||||
Render *render = proxy->render;
|
|
||||||
|
|
||||||
Tile *tile;
|
|
||||||
|
|
||||||
/* Has render been freed?
|
|
||||||
*/
|
|
||||||
if( !render )
|
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
/* Conceptually we work like region_fill(): we free all painted tiles.
|
|
||||||
*/
|
|
||||||
g_mutex_lock( render->read_lock );
|
|
||||||
|
|
||||||
while( (tile = render_tile_get_painted( render )) ) {
|
|
||||||
render->cache = g_slist_remove( render->cache, tile );
|
|
||||||
|
|
||||||
#ifdef DEBUG_REUSE
|
|
||||||
g_mutex_lock( render->dirty_lock );
|
|
||||||
g_assert( !g_slist_find( render->dirty, tile ) );
|
|
||||||
g_mutex_unlock( render->dirty_lock );
|
|
||||||
#endif /*DEBUG_REUSE*/
|
|
||||||
|
|
||||||
tile->render->ntiles -= 1;
|
|
||||||
tile_free( tile );
|
|
||||||
}
|
|
||||||
|
|
||||||
g_mutex_unlock( render->read_lock );
|
|
||||||
|
|
||||||
return( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static Render *
|
static Render *
|
||||||
render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
|
render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
|
||||||
int width, int height, int max,
|
int width, int height, int max,
|
||||||
@ -679,11 +608,6 @@ render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
|
|||||||
render->notify = notify;
|
render->notify = notify;
|
||||||
render->client = client;
|
render->client = client;
|
||||||
|
|
||||||
/* This needs the same lifetime as in, since it's the arg to the
|
|
||||||
* invalidate callbcak.
|
|
||||||
*/
|
|
||||||
render->proxy = IM_NEW( in, RenderProxy );
|
|
||||||
|
|
||||||
render->read_lock = g_mutex_new();
|
render->read_lock = g_mutex_new();
|
||||||
|
|
||||||
render->cache = NULL;
|
render->cache = NULL;
|
||||||
@ -696,19 +620,12 @@ render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
|
|||||||
render->tg = NULL;
|
render->tg = NULL;
|
||||||
render->render_kill = FALSE;
|
render->render_kill = FALSE;
|
||||||
|
|
||||||
if( !render->proxy ||
|
if( im_add_close_callback( out,
|
||||||
im_add_close_callback( out,
|
(im_callback_fn) render_unref, render, NULL ) ) {
|
||||||
(im_callback_fn) render_unref, render, NULL ) ) {
|
|
||||||
(void) render_unref( render );
|
(void) render_unref( render );
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
render->proxy->render = render;
|
|
||||||
|
|
||||||
if( im_add_invalidate_callback( in,
|
|
||||||
(im_callback_fn) render_invalidate, render->proxy, NULL ) )
|
|
||||||
return( NULL );
|
|
||||||
|
|
||||||
return( render );
|
return( render );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,8 +676,10 @@ tile_test_area( Tile *tile, Rect *area )
|
|||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search the cache for a tile, NULL if not there. Could be *much* faster. If
|
/* Search the cache for a tile, NULL if not there. Could be *much* faster.
|
||||||
* we find a tile, bump to front.
|
*
|
||||||
|
* This is always called from the downstream thread, and upstream never adds
|
||||||
|
* or positions tiles, so no need to lock.
|
||||||
*/
|
*/
|
||||||
static Tile *
|
static Tile *
|
||||||
render_tile_lookup( Render *render, Rect *area )
|
render_tile_lookup( Render *render, Rect *area )
|
||||||
@ -900,7 +819,20 @@ render_tile_get( Render *render, Rect *area )
|
|||||||
printf( "render_tile_get: found %dx%d in cache\n",
|
printf( "render_tile_get: found %dx%d in cache\n",
|
||||||
area->left, area->top );
|
area->left, area->top );
|
||||||
#endif /*DEBUG_PAINT*/
|
#endif /*DEBUG_PAINT*/
|
||||||
render_tile_touch( tile );
|
|
||||||
|
/* If the tile is painted but invalid, send it for
|
||||||
|
* calculation.
|
||||||
|
*
|
||||||
|
* Otherwise just touch it to keep it in cache a little
|
||||||
|
* longer.
|
||||||
|
*/
|
||||||
|
if( tile->state == TILE_PAINTED &&
|
||||||
|
tile->region->invalid ) {
|
||||||
|
tile->state = TILE_WORKING;
|
||||||
|
tile_set_dirty( tile, area );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
render_tile_touch( tile );
|
||||||
|
|
||||||
return( tile );
|
return( tile );
|
||||||
}
|
}
|
||||||
@ -943,18 +875,19 @@ tile_copy( Tile *tile, REGION *to )
|
|||||||
{
|
{
|
||||||
Rect ovlap;
|
Rect ovlap;
|
||||||
int y;
|
int y;
|
||||||
int len;
|
|
||||||
|
|
||||||
/* Find common pixels.
|
/* Find common pixels.
|
||||||
*/
|
*/
|
||||||
im_rect_intersectrect( &tile->area, &to->valid, &ovlap );
|
im_rect_intersectrect( &tile->area, &to->valid, &ovlap );
|
||||||
g_assert( !im_rect_isempty( &ovlap ) );
|
g_assert( !im_rect_isempty( &ovlap ) );
|
||||||
len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width;
|
|
||||||
|
|
||||||
/* If the tile is painted, copy over the pixels. Otherwise, fill with
|
/* If the tile is painted, copy over the pixels. Otherwise, fill with
|
||||||
* zero.
|
* zero.
|
||||||
*/
|
*/
|
||||||
if( tile->state == TILE_PAINTED ) {
|
if( tile->state == TILE_PAINTED &&
|
||||||
|
!tile->region->invalid ) {
|
||||||
|
int len = IM_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width;
|
||||||
|
|
||||||
#ifdef DEBUG_PAINT
|
#ifdef DEBUG_PAINT
|
||||||
printf( "tile_copy: copying calculated pixels for %dx%d\n",
|
printf( "tile_copy: copying calculated pixels for %dx%d\n",
|
||||||
tile->area.left, tile->area.top );
|
tile->area.left, tile->area.top );
|
||||||
@ -973,14 +906,7 @@ tile_copy( Tile *tile, REGION *to )
|
|||||||
printf( "tile_copy: zero filling for %dx%d\n",
|
printf( "tile_copy: zero filling for %dx%d\n",
|
||||||
tile->area.left, tile->area.top );
|
tile->area.left, tile->area.top );
|
||||||
#endif /*DEBUG_PAINT*/
|
#endif /*DEBUG_PAINT*/
|
||||||
|
im_region_paint( to, &ovlap, 0 );
|
||||||
for( y = ovlap.top;
|
|
||||||
y < IM_RECT_BOTTOM( &ovlap ); y++ ) {
|
|
||||||
PEL *q = (PEL *)
|
|
||||||
IM_REGION_ADDR( to, ovlap.left, y );
|
|
||||||
|
|
||||||
memset( q, 0, len );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,7 +984,6 @@ mask_fill( REGION *out, void *seq, void *a, void *b )
|
|||||||
for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) {
|
for( x = xs; x < IM_RECT_RIGHT( r ); x += render->width ) {
|
||||||
Rect area;
|
Rect area;
|
||||||
Tile *tile;
|
Tile *tile;
|
||||||
int value;
|
|
||||||
|
|
||||||
area.left = x;
|
area.left = x;
|
||||||
area.top = y;
|
area.top = y;
|
||||||
@ -1066,8 +991,13 @@ mask_fill( REGION *out, void *seq, void *a, void *b )
|
|||||||
area.height = render->height;
|
area.height = render->height;
|
||||||
|
|
||||||
tile = render_tile_lookup( render, &area );
|
tile = render_tile_lookup( render, &area );
|
||||||
value = (tile && tile->state == TILE_PAINTED) ? 255 : 0;
|
|
||||||
im_region_paint( out, &area, value );
|
/* Only mark painted tiles containing valid pixels.
|
||||||
|
*/
|
||||||
|
im_region_paint( out, &area,
|
||||||
|
(tile &&
|
||||||
|
tile->state == TILE_PAINTED &&
|
||||||
|
!tile->region->invalid) ? 255 : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mutex_unlock( render->read_lock );
|
g_mutex_unlock( render->read_lock );
|
||||||
|
Loading…
Reference in New Issue
Block a user