diff --git a/ChangeLog b/ChangeLog index 3e577737..3e35979a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,8 @@ - added im_mpercent_hist() - im_maplut() casts the index image to one of the uint types - fixed a couple of /0 problems with scale == 0 masks +- set G_LOG_DOMAIN to VIPS so we can use g_warning etc. +- added VIPS_DEBUG_MSG() macro 16/1/10 started 7.21.2 - "invalidate" is careful to keep images alive, so invalidate callbacks can do diff --git a/TODO b/TODO index d08a424a..7d986d5c 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,22 @@ +- how about + + #define VIPS_DEBUG_MSG( STR ) printf( ...) + + nicer than lots of #ifdef DEBUG ... + + +- vips_sink() needs to be able to supply a thread start / stop function + + it needs to be able to start and stop sequences, and to guarantee that stops + get called in all paths + + 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 + + + + - look at moving im_iterate() to thereadpool split im_render into two parts: the asynch tile renderer, and the thing to diff --git a/configure.in b/configure.in index 949a0df5..1fbf77cb 100644 --- a/configure.in +++ b/configure.in @@ -42,6 +42,8 @@ AC_SUBST(LIBRARY_AGE) AC_CANONICAL_HOST +AC_DEFINE_UNQUOTED(G_LOG_DOMAIN, "VIPS", [Domain for glib logging messages.]) + AC_MSG_CHECKING([for native Win32]) case "$host" in *-*-mingw*) diff --git a/libvips/include/vips/debug.h b/libvips/include/vips/debug.h index 2d150d0f..cc285537 100644 --- a/libvips/include/vips/debug.h +++ b/libvips/include/vips/debug.h @@ -34,6 +34,30 @@ extern "C" { #endif /*__cplusplus*/ +#ifdef VIPS_DEBUG +#define VIPS_DEBUG_MSG( FMT, ... ) printf( FMT, __VA_ARGS__ ) +#else +#define VIPS_DEBUG_MSG( FMT, ... ) +#endif /*VIPS_DEBUG*/ + +#ifdef VIPS_DEBUG_RED +#define VIPS_DEBUG_MSG_RED( FMT, ... ) printf( "red: " FMT, __VA_ARGS__ ) +#else +#define VIPS_DEBUG_MSG_RED( FMT, ... ) +#endif /*VIPS_DEBUG_RED*/ + +#ifdef VIPS_DEBUG_AMBER +#define VIPS_DEBUG_MSG_AMBER( FMT, ... ) printf( "amber: " FMT, __VA_ARGS__ ) +#else +#define VIPS_DEBUG_MSG_AMBER( FMT, ... ) +#endif /*VIPS_DEBUG_AMBER*/ + +#ifdef VIPS_DEBUG_GREEN +#define VIPS_DEBUG_MSG_GREEN( FMT, ... ) printf( "green: " FMT, __VA_ARGS__ ) +#else +#define VIPS_DEBUG_MSG_GREEN( FMT, ... ) +#endif /*VIPS_DEBUG_GREEN*/ + /* All open image descriptors ... see im_init() and im_close(). */ extern GSList *im__open_images; diff --git a/libvips/include/vips/threadpool.h b/libvips/include/vips/threadpool.h index 14ab0e8c..08c2571d 100644 --- a/libvips/include/vips/threadpool.h +++ b/libvips/include/vips/threadpool.h @@ -51,7 +51,7 @@ typedef struct _VipsThreadState { */ REGION *reg; - /* The rest are neither used nor set, do what you lke with them. + /* The rest are neither used nor set, do what you like with them. */ Rect pos; int x, y; diff --git a/libvips/iofuncs/sink.c b/libvips/iofuncs/sink.c new file mode 100644 index 00000000..d6d05562 --- /dev/null +++ b/libvips/iofuncs/sink.c @@ -0,0 +1,280 @@ +/* A sink that's not attached to anything, eg. find image average, + * + * 28/3/10 + * - from im_iterate(), reworked for threadpool + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/* Per-call state. + */ +typedef struct _Sink { + VipsImage *im; + + /* We need a temp "p" image between the source image and us to + * make sure we can't damage the original. + */ + VipsImage *t; + + /* The position we're at in the image. + */ + int x; + int y; + + /* The tilesize we've picked. + */ + int tile_width; + int tile_height; + int nlines; + + /* Keep the sequence value in VipsThreadState->d. + */ + im_start_fn start; + im_generate_fn generate; + im_stop_fn stop; + void *a; + void *b; +} Sink; + +static void +sink_free( Sink *sink ) +{ + IM_FREEF( im_close, sink->t ); +} + +static int +sink_init( Sink *sink, + VipsImage *im, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + void *a, void *b ) +{ + sink->im = im; + sink->t = NULL; + sink->start = start; + sink->generate = generate; + sink->stop = stop; + sink->a = a; + sink->b = b; + + if( !(sink->t = im_open( "iterate", "p" )) || + im_copy( sink->im, sink->t ) ) { + sink_free( sink ); + return( -1 ); + } + + vips_get_tile_size( im, + &sink->tile_width, &sink->tile_height, &sink->nlines ); + + return( 0 ); +} + +/* Call the start function for this thread, if necessary. + */ +static int +sink_call_start( Sink *sink, VipsThreadState *state ) +{ + if( !state->d && sink->start ) { +#ifdef DEBUG + printf( "sink_call_start: state = %p\n", state ); +#endif /*DEBUG*/ + state->d = sink->start( sink->t, sink->a, sink->b ); + + if( !state->d ) { + im_error( "vips_sink", + _( "start function failed for image \"%s\"" ), + sink->im->filename ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Call a thread's stop function. + */ +static int +sink_call_stop( Sink *sink, VipsThreadState *state ) +{ + if( state->d && sink->stop ) { +#ifdef DEBUG + printf( "sink_call_stop: state = %p\n", state ); +#endif /*DEBUG*/ + + if( sink->stop( state->d, sink->a, sink->b ) ) { + im_error( "vips_sink", + _( "stop function failed for image \"%s\"" ), + sink->im->filename ); + return( -1 ); + } + + state->d = NULL; + } + + return( 0 ); +} + +static int +sink_allocate( VipsThreadState *state, + void *a, void *b, void *c, gboolean *stop ) +{ + Sink *sink = (Sink *) a; + + Rect image, tile; + + /* Is the state x/y OK? New line or maybe all done. + */ + if( sink->x >= sink->im->Xsize ) { + sink->x = 0; + sink->y += sink->tile_height; + + if( sink->y >= sink->im->Ysize ) { + sink_call_stop( sink, state ); + *stop = TRUE; + return( 0 ); + } + } + + sink_call_start( sink, state ); + + /* x, y and buf are good: save params for thread. + */ + image.left = 0; + image.top = 0; + image.width = sink->im->Xsize; + image.height = sink->im->Ysize; + tile.left = sink->x; + tile.top = sink->y; + tile.width = sink->tile_width; + tile.height = sink->tile_height; + im_rect_intersectrect( &image, &tile, &state->pos ); + + /* Move state on. + */ + write->x += write->tile_width; + + return( 0 ); +} + +static int +sink_work( VipsThreadState *state, void *a, void *b, void *c ) +{ + Sink *sink = (Sink *) a; + + if( im_prepare( state->reg, &state->pos ) || + sink->generate( state->reg, state->d, sink->a, sink->b ) ) { + sink_call_stop( sink, state ); + return( -1 ); + } + + return( 0 ); +} + +static int +sink_progress( void *a, void *b, void *c ) +{ + Sink *sink = (Sink *) a; + + /* Trigger any eval callbacks on our source image and + * check for errors. + */ + if( im__handle_eval( sink->im, + sink->tile_width, sink->tile_height ) ) + return( -1 ); + + return( 0 ); +} + +/** + * vips_sink: + * @im: scan over this image + * @start: start sequences with this function + * @generate: generate pixels with this function + * @stop: stop sequences with this function + * @a: user data + * @b: user data + * + * Loops over an image. @generate is called for every pixel in the image, with + * the @reg argument being a region of pixels for processing. im_iterate() is + * used to implement operations like im_avg() which have no image output. + * + * See also: im_generate(), im_open(). + * + * Returns: 0 on success, or -1 on error. + */ +int +vips_sink( VipsImage *im, + VipsStart start, VipsGenerate generate, VipsStop stop, + void *a, void *b ) +{ + Sink sink; + int result; + + g_assert( !im_image_sanity( im ) ); + + /* We don't use this, but make sure it's set in case any old binaries + * are expecting it. + */ + im->Bbits = im_bits_of_fmt( im->BandFmt ); + + if( sink_init( &sink, im, start, generate, stop, a, b ) ) + return( -1 ); + + if( im__start_eval( sink.t ) ) { + sink_free( &sink ); + return( -1 ); + } + + result = vips_threadpool_run( im, + sink_allocate, sink_work, sink_progress, &sink, NULL ); + + im__end_eval( sink.t ); + + sink_free( &sink ); + + return( result ); +} diff --git a/libvips/iofuncs/threadpool.c b/libvips/iofuncs/threadpool.c index 8fba6ef1..e8273575 100644 --- a/libvips/iofuncs/threadpool.c +++ b/libvips/iofuncs/threadpool.c @@ -37,8 +37,8 @@ /* #define TIME_THREAD -#define DEBUG_CREATE -#define DEBUG_IO +#define VIPS_DEBUG_RED +#define VIPS_DEBUG */ #ifdef HAVE_CONFIG_H @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef WITH_DMALLOC #include @@ -186,10 +187,7 @@ vips_thread_free( VipsThread *thr ) /* Return value is always NULL (see thread_main_loop). */ (void) g_thread_join( thr->thread ); -#ifdef DEBUG_CREATE - printf( "thread_free: g_thread_join()\n" ); -#endif /*DEBUG_CREATE*/ - + VIPS_DEBUG_MSG_RED( "thread_free: g_thread_join()\n" ); thr->thread = NULL; } @@ -342,9 +340,7 @@ vips_thread_new( VipsThreadpool *pool ) return( NULL ); } -#ifdef DEBUG_CREATE - printf( "vips_thread_new: g_thread_create_full()\n" ); -#endif /*DEBUG_CREATE*/ + VIPS_DEBUG_MSG_RED( "vips_thread_new: g_thread_create_full()\n" ); #endif /*HAVE_THREADS*/ return( thr ); @@ -362,20 +358,16 @@ vips_threadpool_kill_threads( VipsThreadpool *pool ) vips_thread_free( pool->thr[i] ); pool->thr = NULL; -#ifdef DEBUG_IO - printf( "vips_threadpool_kill_threads: killed %d threads\n", - pool->nthr ); -#endif /*DEBUG_IO*/ + VIPS_DEBUG_MSG( "vips_threadpool_kill_threads: " + "killed %d threads\n", pool->nthr ); } } static int vips_threadpool_free( VipsThreadpool *pool ) { -#ifdef DEBUG_IO - printf( "vips_threadpool_free: \"%s\" (%p)\n", + VIPS_DEBUG_MSG( "vips_threadpool_free: \"%s\" (%p)\n", pool->im->filename, pool ); -#endif /*DEBUG_IO*/ vips_threadpool_kill_threads( pool ); IM_FREEF( g_mutex_free, pool->allocate_lock ); @@ -413,10 +405,8 @@ vips_threadpool_new( VipsImage *im ) return( NULL ); } -#ifdef DEBUG_IO - printf( "vips_threadpool_new: \"%s\" (%p), with %d threads\n", + VIPS_DEBUG_MSG( "vips_threadpool_new: \"%s\" (%p), with %d threads\n", im->filename, pool, pool->nthr ); -#endif /*DEBUG_IO*/ return( pool ); } @@ -668,10 +658,8 @@ vips_get_tile_size( VipsImage *im, */ g_assert( *nlines % *tile_height == 0 ); -#ifdef DEBUG_IO - printf( "vips_get_tile_size: %d by %d patches, " + VIPS_DEBUG_MSG( "vips_get_tile_size: %d by %d patches, " "groups of %d scanlines\n", *tile_width, *tile_height, *nlines ); -#endif /*DEBUG_IO*/ }