vips_sink_screen() hacking

This commit is contained in:
John Cupitt 2010-04-10 08:35:55 +00:00
parent 56c1c9c036
commit eb4d3f9fd5
3 changed files with 72 additions and 89 deletions

24
TODO
View File

@ -1,30 +1,18 @@
- vips_threadpool() has three clientdata, do we need all of them? maube one is
enough
rename vips_discsink() / discsink.c as vips_sink_disc / sinkdisc
- vips_sink() needs to be able to supply a thread start / stop function
- we need to be able to stop a threadpool from outside
it needs to be able to start and stop sequences, and to guarantee that stops
get called in all paths
when a request comes in to vips_sink_screen(), we want to stop the
vips_threadpool() currently executing and start a new one
for example, if one thread sets stop in allocate, not all threads will get
to run again, they can be shut down by vips_threadpool first
perhaps progress should have a 'stop' param as well?
can progress be NULL?
- look at moving im_iterate() to thereadpool
split im_render into two parts: the asynch tile renderer, and the thing to
keep a queue of current render operations
the new tile render needs some easy way to start/stop it, preserving the
list of dirty tiles
argh, maybe it does need to be in one large operation
- expose more of the tone funcs in nip2

View File

@ -134,11 +134,11 @@ int vips_sink( VipsImage *im,
VipsStart start, VipsGenerate generate, VipsStop stop,
void *a, void *b );
typedef void (*VipsSinkNotify)( VipsImage *im, Rect *rect, void *client );
typedef void (*VipsSinkNotify)( VipsImage *im, Rect *rect, void *a );
int vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask,
int width, int height, int max,
int priority,
VipsSinkNotify notify, void *client );
VipsSinkNotify notify, void *a );
#ifdef __cplusplus
}

View File

@ -31,10 +31,7 @@
*/
/* Turn on debugging output.
#define DEBUG
#define DEBUG_MAKE
#define DEBUG_REUSE
#define DEBUG_PAINT
#define VIPS_DEBUG
*/
#ifdef HAVE_CONFIG_H
@ -68,9 +65,18 @@ static const int have_threads = 0;
/* The states a tile can be in.
*/
typedef enum {
TILE_DIRTY, /* On the dirty list .. contains no pixels */
TILE_WORKING, /* Currently being worked on */
TILE_PAINTED /* Painted, ready for reuse */
/* On the dirty list .. contains no pixels
*/
TILE_DIRTY,
/* Currently being worked on .. not on the dirty list, but contains
* no valid pixels.
*/
TILE_WORKING,
/* Valid pixels, not on dirty.
*/
TILE_PAINTED
} TileState;
/* A tile in our cache.
@ -78,28 +84,19 @@ typedef enum {
typedef struct {
struct _Render *render;
TileState state;
Rect area; /* Place here (unclipped) */
REGION *region; /* REGION with the pixels */
int access_ticks; /* Time of last use for LRU flush */
int time; /* Time when we finished painting */
TileState state;
} Tile;
/*
FIXME ... should have an LRU queue rather than this thing with times
FIXME ... could also hash from tile xy to tile pointer
*/
/* Per-call state.
*/
typedef struct _Render {
/* Reference count this, since we use these things from several
* threads. Can't easily use the gobject ref count system since we
* threads. We can't easily use the gobject ref count system since we
* need a lock around operations.
*/
int ref_count;
@ -107,14 +104,15 @@ typedef struct _Render {
/* Parameters.
*/
IMAGE *in; /* Image we render */
IMAGE *out; /* Write tiles here on demand */
IMAGE *mask; /* Set valid pixels here */
int width, height; /* Tile size */
int max; /* Maximum number of tiles */
VipsImage *in; /* Image we render */
VipsImage *out; /* Write tiles here on demand */
VipsImage *mask; /* Set valid pixels here */
int tile_width; /* Tile size */
int tile_height;
int max_tiles; /* Maximum number of tiles */
int priority; /* Larger numbers done sooner */
notify_fn notify; /* Tell caller about paints here */
void *client;
VipsSinkNotify notify; /* Tell caller about paints here */
void *a;
/* Make readers single thread with this. No point allowing
* multi-thread read.
@ -123,7 +121,7 @@ typedef struct _Render {
/* Tile cache.
*/
GSList *cache; /* List of all our tiles */
GHashTable *cache; /* All our tiles, hash from x/y pos */
int ntiles; /* Number of cache tiles */
int access_ticks; /* Inc. on each access ... used for LRU */
@ -131,20 +129,13 @@ typedef struct _Render {
*/
GMutex *dirty_lock; /* Lock before we read/write the dirty list */
GSList *dirty; /* Tiles which need painting */
/* Render thread stuff.
*/
im_threadgroup_t *tg; /* Render with this threadgroup */
int render_kill; /* This render is dying */
} Render;
/* Number of RenderThread we create.
/* The BG thread which sits waiting to do some rendering.
*/
static const int render_thread_max = 1;
static GThread *render_thread = NULL;
static GSList *render_thread_all = NULL;
/* Number of renders with dirty tiles. RenderThreads queue up on this.
/* Number of renders with dirty tiles. render_thread queues up on this.
*/
static im_semaphore_t render_dirty_sem;
@ -161,48 +152,24 @@ render_dirty_remove( Render *render )
if( g_slist_find( render_dirty_all, render ) ) {
render_dirty_all = g_slist_remove( render_dirty_all, render );
/* We know this can't block since there is at least 1 item in
* the render_dirty_all list (us). Except that
* render_dirty_get() does a _down() before it locks so this
* could block if we run inbetween :-( possible deadlock.
*/
im_semaphore_down( &render_dirty_sem );
im_semaphore_upn( &render_dirty_sem, -1 );
}
g_mutex_unlock( render_dirty_lock );
}
static void *
tile_free( Tile *tile )
{
#ifdef DEBUG_MAKE
printf( "tile_free\n" );
#endif /*DEBUG_MAKE*/
IM_FREEF( im_region_free, tile->region );
im_free( tile );
return( NULL );
}
static int
render_free( Render *render )
{
#ifdef DEBUG_MAKE
printf( "render_free: %p\n", render );
#endif /*DEBUG_MAKE*/
VIPS_DEBUG_MSG_RED( "render_free: %p\n", render );
g_assert( render->ref_count == 0 );
render_dirty_remove( render );
IM_FREEF( im_threadgroup_free, render->tg );
/* Free cache.
*/
im_slist_map2( render->cache,
(VSListMap2Fn) tile_free, NULL, NULL );
IM_FREEF( g_slist_free, render->cache );
IM_FREEF( g_hash_table_destroy, render->cache );
render->ntiles = 0;
IM_FREEF( g_slist_free, render->dirty );
@ -516,6 +483,37 @@ render_tile_get_painted( Render *render )
return( tile );
}
static guint
tile_hash( gconstpointer key )
{
Tile *tile = (Tile *) key;
int x = tile->area.left / tile->render->tile_width;
int y = tile->area.top / tile->render->tile_height;
return( x << 16 ^ y );
}
static gboolean
tile_equal( gconstpointer a, gconstpointer b )
{
Tile *tile1 = (Tile *) a;
Tile *tile2 = (Tile *) b;
return( tile1->area.left == tile2->area.left &&
tile1->area.top == tile2->area.top );
}
static void *
tile_free( Tile *tile )
{
VIPS_DEBUG_MSG_RED( "tile_free\n" );
IM_FREEF( im_region_free, tile->region );
im_free( tile );
return( NULL );
}
static Render *
render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
int width, int height, int max,
@ -545,16 +543,14 @@ render_new( IMAGE *in, IMAGE *out, IMAGE *mask,
render->read_lock = g_mutex_new();
render->cache = NULL;
render->cache = g_hash_table_new_full( tile_hash, tile_equal,
tile_free, NULL );
render->ntiles = 0;
render->access_ticks = 0;
render->dirty_lock = g_mutex_new();
render->dirty = NULL;
render->tg = NULL;
render->render_kill = FALSE;
if( im_add_close_callback( out,
(im_callback_fn) render_unref, render, NULL ) ) {
(void) render_unref( render );
@ -588,7 +584,6 @@ tile_new( Render *render )
tile->area.width = 0;
tile->area.height = 0;
tile->access_ticks = render->access_ticks;
tile->time = 0;
tile->state = TILE_WORKING;
if( !(tile->region = im_region_create( render->in )) ) {