1 /* aynchronous screen sink
2  *
3  * 1/1/10
4  * 	- from im_render.c
5  * 25/11/10
6  * 	- in synchronous mode, use a single region for input and save huge
7  * 	  mem use
8  * 20/1/14
9  * 	- bg render thread quits on shutdown
10  * 1/12/15
11  * 	- don't do anything to out or mask after they have closed
12  * 	- only run the bg render thread when there's work to do
13  */
14 
15 /*
16 
17     This file is part of VIPS.
18 
19     VIPS is free software; you can redistribute it and/or modify
20     it under the terms of the GNU Lesser General Public License as published by
21     the Free Software Foundation; either version 2 of the License, or
22     (at your option) any later version.
23 
24     This program is distributed in the hope that it will be useful,
25     but WITHOUT ANY WARRANTY; without even the implied warranty of
26     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27     GNU Lesser General Public License for more details.
28 
29     You should have received a copy of the GNU Lesser General Public License
30     along with this program; if not, write to the Free Software
31     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32     02110-1301  USA
33 
34  */
35 
36 /*
37 
38     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
39 
40  */
41 
42 /* Verbose debugging output.
43 #define VIPS_DEBUG
44  */
45 
46 /* Trace allocate/free.
47 #define VIPS_DEBUG_AMBER
48  */
49 
50 /* Trace reschedule
51 #define VIPS_DEBUG_GREEN
52  */
53 
54 /* Trace serious problems.
55 #define VIPS_DEBUG_RED
56  */
57 
58 #ifdef HAVE_CONFIG_H
59 #include <config.h>
60 #endif /*HAVE_CONFIG_H*/
61 #include <vips/intl.h>
62 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <math.h>
67 #ifdef HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif /*HAVE_UNISTD_H*/
70 
71 #include <vips/vips.h>
72 #include <vips/thread.h>
73 #include <vips/internal.h>
74 #include <vips/debug.h>
75 
76 #ifdef VIPS_DEBUG_AMBER
77 static int render_num_renders = 0;
78 #endif /*VIPS_DEBUG_AMBER*/
79 
80 /* A tile in our cache.
81  */
82 typedef struct {
83 	struct _Render *render;
84 
85 	VipsRect area;		/* Place here (unclipped) */
86 	VipsRegion *region;	/* VipsRegion with the pixels */
87 
88 	/* The tile contains calculated pixels. Though the region may have been
89 	 * invalidated behind our backs: we have to check that too.
90 	 */
91 	gboolean painted;
92 
93 	/* The tile is on the dirty list. This saves us having to search the
94 	 * dirty list all the time.
95 	 */
96 	gboolean dirty;
97 
98 	/* Time of last use, for LRU flush
99 	 */
100 	int ticks;
101 } Tile;
102 
103 /* Per-call state.
104  */
105 typedef struct _Render {
106 	/* Reference count this, since we use these things from several
107 	 * threads. We can't easily use the gobject ref count system since we
108 	 * need a lock around operations.
109 	 */
110 #if GLIB_CHECK_VERSION( 2, 58, 0 )
111 	gatomicrefcount ref_count;
112 #else
113 	int ref_count;
114 	GMutex *ref_count_lock;
115 #endif
116 
117 	/* Parameters.
118 	 */
119 	VipsImage *in;		/* Image we render */
120 	VipsImage *out;		/* Write tiles here on demand */
121 	VipsImage *mask;	/* Set valid pixels here */
122 	int tile_width;		/* Tile size */
123 	int tile_height;
124 	int max_tiles;		/* Maximum number of tiles */
125 	int priority;		/* Larger numbers done sooner */
126 	VipsSinkNotify notify;	/* Tell caller about paints here */
127 	void *a;
128 
129 	/* Lock here before reading or modifying the tile structure.
130 	 */
131 	GMutex *lock;
132 
133 	/* Tile cache.
134 	 */
135 	GSList *all;		/* All our tiles */
136 	int ntiles;		/* Number of tiles */
137 	int ticks;		/* Inc. on each access ... used for LRU */
138 
139 	/* List of dirty tiles. Most recent at the front.
140 	 */
141 	GSList *dirty;
142 
143 	/* Hash of tiles with positions. Tiles can be dirty or painted.
144 	 */
145 	GHashTable *tiles;
146 
147 	/* A shutdown flag. If ->out or ->mask close, we must no longer do
148 	 * anything to them until we shut down too.
149 	 */
150 	gboolean shutdown;
151 } Render;
152 
153 /* Our per-thread state.
154  */
155 typedef struct _RenderThreadState {
156 	VipsThreadState parent_object;
157 
158 	/* The tile that should be calculated.
159 	 */
160 	Tile *tile;
161 } RenderThreadState;
162 
163 typedef struct _RenderThreadStateClass {
164 	VipsThreadStateClass parent_class;
165 
166 } RenderThreadStateClass;
167 
168 G_DEFINE_TYPE( RenderThreadState, render_thread_state, VIPS_TYPE_THREAD_STATE );
169 
170 /* The BG thread which sits waiting to do some calculations, and the semaphore
171  * it waits on holding the number of renders with dirty tiles.
172  */
173 static GThread *render_thread = NULL;
174 
175 /* Set this to ask the render thread to quit.
176  */
177 static gboolean render_kill = FALSE;
178 
179 /* All the renders with dirty tiles, and a semaphore that the bg render thread
180  * waits on.
181  */
182 static GMutex *render_dirty_lock = NULL;
183 static GSList *render_dirty_all = NULL;
184 static VipsSemaphore n_render_dirty_sem;
185 
186 /* Set this to make the bg thread stop and reschedule.
187  */
188 static gboolean render_reschedule = FALSE;
189 
190 static void
render_thread_state_class_init(RenderThreadStateClass * class)191 render_thread_state_class_init( RenderThreadStateClass *class )
192 {
193 	VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
194 
195 	object_class->nickname = "renderthreadstate";
196 	object_class->description = _( "per-thread state for render" );
197 }
198 
199 static void
render_thread_state_init(RenderThreadState * state)200 render_thread_state_init( RenderThreadState *state )
201 {
202 	state->tile = NULL;
203 }
204 
205 static VipsThreadState *
render_thread_state_new(VipsImage * im,void * a)206 render_thread_state_new( VipsImage *im, void *a )
207 {
208 	return( VIPS_THREAD_STATE( vips_object_new(
209 		render_thread_state_get_type(),
210 		vips_thread_state_set, im, a ) ) );
211 }
212 
213 static void *
tile_free(Tile * tile,void * a,void * b)214 tile_free( Tile *tile, void *a, void *b )
215 {
216 	VIPS_DEBUG_MSG_AMBER( "tile_free\n" );
217 
218 	VIPS_UNREF( tile->region );
219 	g_free( tile );
220 
221 	return( NULL );
222 }
223 
224 static int
render_free(Render * render)225 render_free( Render *render )
226 {
227 	VIPS_DEBUG_MSG_AMBER( "render_free: %p\n", render );
228 
229 #if GLIB_CHECK_VERSION( 2, 58, 0 )
230 	g_assert ( g_atomic_ref_count_compare( &render->ref_count, 0 ) );
231 #else
232 	g_assert( render->ref_count == 0 );
233 #endif
234 
235 	g_mutex_lock( render_dirty_lock );
236 	if( g_slist_find( render_dirty_all, render ) ) {
237 		render_dirty_all = g_slist_remove( render_dirty_all, render );
238 
239 		/* We don't need to adjust the semaphore: if it's too high,
240 		 * the render thread will just loop and decrement next time
241 		 * render_dirty_all is NULL.
242 		 */
243 	}
244 	g_mutex_unlock( render_dirty_lock );
245 
246 #if !GLIB_CHECK_VERSION( 2, 58, 0 )
247 	vips_g_mutex_free( render->ref_count_lock );
248 #endif
249 	vips_g_mutex_free( render->lock );
250 
251 	vips_slist_map2( render->all, (VipsSListMap2Fn) tile_free, NULL, NULL );
252 	VIPS_FREEF( g_slist_free, render->all );
253 	render->ntiles = 0;
254 	VIPS_FREEF( g_slist_free, render->dirty );
255 	VIPS_FREEF( g_hash_table_destroy, render->tiles );
256 
257 	VIPS_UNREF( render->in );
258 
259 	g_free( render );
260 
261 #ifdef VIPS_DEBUG_AMBER
262 	render_num_renders -= 1;
263 #endif /*VIPS_DEBUG_AMBER*/
264 
265 	return( 0 );
266 }
267 
268 /* Ref and unref a Render ... free on last unref.
269  */
270 static int
render_ref(Render * render)271 render_ref( Render *render )
272 {
273 #if GLIB_CHECK_VERSION( 2, 58, 0 )
274 	g_assert( !g_atomic_ref_count_compare( &render->ref_count, 0 ) );
275 	g_atomic_ref_count_inc( &render->ref_count );
276 #else
277 	g_mutex_lock( render->ref_count_lock );
278 	g_assert( render->ref_count != 0 );
279 	render->ref_count += 1;
280 	g_mutex_unlock( render->ref_count_lock );
281 #endif
282 
283 	return( 0 );
284 }
285 
286 static int
render_unref(Render * render)287 render_unref( Render *render )
288 {
289 	int kill;
290 
291 #if GLIB_CHECK_VERSION( 2, 58, 0 )
292 	g_assert( !g_atomic_ref_count_compare( &render->ref_count, 0 ) );
293 	kill = g_atomic_ref_count_dec( &render->ref_count );
294 #else
295 	g_mutex_lock( render->ref_count_lock );
296 	g_assert( render->ref_count > 0 );
297 	render->ref_count -= 1;
298 	kill = render->ref_count == 0;
299 	g_mutex_unlock( render->ref_count_lock );
300 #endif
301 
302 	if( kill )
303 		render_free( render );
304 
305 	return( 0 );
306 }
307 
308 /* Get the next tile to paint off the dirty list.
309  */
310 static Tile *
render_tile_dirty_get(Render * render)311 render_tile_dirty_get( Render *render )
312 {
313 	Tile *tile;
314 
315 	if( !render->dirty )
316 		tile = NULL;
317 	else {
318 		tile = (Tile *) render->dirty->data;
319 		g_assert( tile->dirty );
320 		render->dirty = g_slist_remove( render->dirty, tile );
321 		tile->dirty = FALSE;
322 	}
323 
324 	return( tile );
325 }
326 
327 /* Pick a dirty tile to reuse. We could potentially get the tile that
328  * render_work() is working on in the background :-( but I don't think we'll
329  * get a crash, just a mis-paint. It should be vanishingly impossible anyway.
330  */
331 static Tile *
render_tile_dirty_reuse(Render * render)332 render_tile_dirty_reuse( Render *render )
333 {
334 	Tile *tile;
335 
336 	if( !render->dirty )
337 		tile = NULL;
338 	else {
339 		tile = (Tile *) g_slist_last( render->dirty )->data;
340 		render->dirty = g_slist_remove( render->dirty, tile );
341 		g_assert( tile->dirty );
342 		tile->dirty = FALSE;
343 
344 		VIPS_DEBUG_MSG( "render_tile_get_dirty_reuse: "
345 			"reusing dirty %p\n", tile );
346 	}
347 
348 	return( tile );
349 }
350 
351 /* Add a tile to the dirty list.
352  */
353 static void
tile_dirty_set(Tile * tile)354 tile_dirty_set( Tile *tile )
355 {
356 	Render *render = tile->render;
357 
358 	if( !tile->dirty ) {
359 		g_assert( !g_slist_find( render->dirty, tile ) );
360 		render->dirty = g_slist_prepend( render->dirty, tile );
361 		tile->dirty = TRUE;
362 		tile->painted = FALSE;
363 	}
364 	else
365 		g_assert( g_slist_find( render->dirty, tile ) );
366 }
367 
368 /* Bump a tile to the front of the dirty list, if it's there.
369  */
370 static void
tile_dirty_bump(Tile * tile)371 tile_dirty_bump( Tile *tile )
372 {
373 	Render *render = tile->render;
374 
375 	if( tile->dirty ) {
376 		g_assert( g_slist_find( render->dirty, tile ) );
377 
378 		render->dirty = g_slist_remove( render->dirty, tile );
379 		render->dirty = g_slist_prepend( render->dirty, tile );
380 	}
381 	else
382 		g_assert( !g_slist_find( render->dirty, tile ) );
383 }
384 
385 static int
render_allocate(VipsThreadState * state,void * a,gboolean * stop)386 render_allocate( VipsThreadState *state, void *a, gboolean *stop )
387 {
388 	Render *render = (Render *) a;
389 	RenderThreadState *rstate = (RenderThreadState *) state;
390 	Tile *tile;
391 
392 	g_mutex_lock( render->lock );
393 
394 	if( render_reschedule ||
395 		!(tile = render_tile_dirty_get( render )) ) {
396 		VIPS_DEBUG_MSG_GREEN( "render_allocate: stopping\n" );
397 		*stop = TRUE;
398 		rstate->tile = NULL;
399 	}
400 	else
401 		rstate->tile = tile;
402 
403 	g_mutex_unlock( render->lock );
404 
405 	return( 0 );
406 }
407 
408 static int
render_work(VipsThreadState * state,void * a)409 render_work( VipsThreadState *state, void *a )
410 {
411 	Render *render = (Render *) a;
412 	RenderThreadState *rstate = (RenderThreadState *) state;
413 	Tile *tile = rstate->tile;
414 
415 	g_assert( tile );
416 
417 	VIPS_DEBUG_MSG( "calculating tile %p %dx%d\n",
418 		tile, tile->area.left, tile->area.top );
419 
420 	if( vips_region_prepare_to( state->reg, tile->region,
421 		&tile->area, tile->area.left, tile->area.top ) ) {
422 		VIPS_DEBUG_MSG_RED( "render_work: "
423 			"vips_region_prepare_to() failed: %s\n",
424 			vips_error_buffer() );
425 		return( -1 );
426 	}
427 	tile->painted = TRUE;
428 
429 	if( !render->shutdown &&
430 		render->notify )
431 		render->notify( render->out, &tile->area, render->a );
432 
433 	return( 0 );
434 }
435 
436 static void render_dirty_put( Render *render );
437 
438 /* Called from vips_shutdown().
439  */
440 void
vips__render_shutdown(void)441 vips__render_shutdown( void )
442 {
443 	/* We may come here without having inited.
444 	 */
445 	if( render_dirty_lock ) {
446 		g_mutex_lock( render_dirty_lock );
447 
448 		if( render_thread ) {
449 			GThread *thread;
450 
451 			thread = render_thread;
452 			render_reschedule = TRUE;
453 			render_kill = TRUE;
454 
455 			g_mutex_unlock( render_dirty_lock );
456 
457 			vips_semaphore_up( &n_render_dirty_sem );
458 
459 			(void) vips_g_thread_join( thread );
460 		}
461 		else
462 			g_mutex_unlock( render_dirty_lock );
463 
464 		VIPS_FREEF( vips_g_mutex_free, render_dirty_lock );
465 		vips_semaphore_destroy( &n_render_dirty_sem );
466 	}
467 }
468 
469 static int
render_dirty_sort(Render * a,Render * b,void * user_data)470 render_dirty_sort( Render *a, Render *b, void *user_data )
471 {
472 	return( b->priority - a->priority );
473 }
474 
475 /* Add to the jobs list, if it has work to be done.
476  */
477 static void
render_dirty_put(Render * render)478 render_dirty_put( Render *render )
479 {
480 	g_mutex_lock( render_dirty_lock );
481 
482 	if( render->dirty ) {
483 		if( !g_slist_find( render_dirty_all, render ) ) {
484 			render_dirty_all = g_slist_prepend( render_dirty_all,
485 				render );
486 			render_dirty_all = g_slist_sort( render_dirty_all,
487 				(GCompareFunc) render_dirty_sort );
488 
489 			/* Tell the bg render thread we have one more dirty
490 			 * render on there.
491 			 */
492 			vips_semaphore_up( &n_render_dirty_sem );
493 		}
494 	}
495 
496 	g_mutex_unlock( render_dirty_lock );
497 }
498 
499 static guint
tile_hash(gconstpointer key)500 tile_hash( gconstpointer key )
501 {
502 	VipsRect *rect = (VipsRect *) key;
503 
504 	int x = rect->left / rect->width;
505 	int y = rect->top / rect->height;
506 
507 	return( x << 16 ^ y );
508 }
509 
510 static gboolean
tile_equal(gconstpointer a,gconstpointer b)511 tile_equal( gconstpointer a, gconstpointer b )
512 {
513 	VipsRect *rect1 = (VipsRect *) a;
514 	VipsRect *rect2 = (VipsRect *) b;
515 
516 	return( rect1->left == rect2->left &&
517 		rect1->top == rect2->top );
518 }
519 
520 static void
render_close_cb(VipsImage * image,Render * render)521 render_close_cb( VipsImage *image, Render *render )
522 {
523 	VIPS_DEBUG_MSG_AMBER( "render_close_cb\n" );
524 
525 	/* The output image or mask are closing. This render will stick
526 	 * around for a while, since threads can still be running, but it
527 	 * must no longer reference ->out or ->mask (for example, invalidating
528 	 * them).
529 	 */
530 	render->shutdown = TRUE;
531 
532 	render_unref( render );
533 
534 	/* If this render is being worked on, we want to jog the bg thread,
535 	 * make it drop it's ref and think again.
536 	 */
537 	VIPS_DEBUG_MSG_GREEN( "render_close_cb: reschedule\n" );
538 	render_reschedule = TRUE;
539 }
540 
541 static Render *
render_new(VipsImage * in,VipsImage * out,VipsImage * mask,int tile_width,int tile_height,int max_tiles,int priority,VipsSinkNotify notify,void * a)542 render_new( VipsImage *in, VipsImage *out, VipsImage *mask,
543 	int tile_width, int tile_height,
544 	int max_tiles,
545 	int priority,
546 	VipsSinkNotify notify, void *a )
547 {
548 	Render *render;
549 
550 	/* Don't use auto-free for render, we do our own lifetime management
551 	 * with _ref() and _unref().
552 	 */
553 	if( !(render = VIPS_NEW( NULL, Render )) )
554 		return( NULL );
555 
556 	/* render must hold a ref to in. This is dropped in render_free().
557 	 */
558 	g_object_ref( in );
559 
560 #if GLIB_CHECK_VERSION( 2, 58, 0 )
561 	g_atomic_ref_count_init( &render->ref_count );
562 #else
563 	render->ref_count = 1;
564 	render->ref_count_lock = vips_g_mutex_new();
565 #endif
566 
567 	render->in = in;
568 	render->out = out;
569 	render->mask = mask;
570 	render->tile_width = tile_width;
571 	render->tile_height = tile_height;
572 	render->max_tiles = max_tiles;
573 	render->priority = priority;
574 	render->notify = notify;
575 	render->a = a;
576 
577 	render->lock = vips_g_mutex_new();
578 
579 	render->all = NULL;
580 	render->ntiles = 0;
581 	render->ticks = 0;
582 
583 	render->tiles = g_hash_table_new( tile_hash, tile_equal );
584 
585 	render->dirty = NULL;
586 
587 	render->shutdown = FALSE;
588 
589 	/* Both out and mask must close before we can free the render.
590 	 */
591 	g_signal_connect( out, "close",
592 		G_CALLBACK( render_close_cb ), render );
593 
594 	if( mask ) {
595 		g_signal_connect( mask, "close",
596 			G_CALLBACK( render_close_cb ), render );
597 		render_ref( render );
598 	}
599 
600 	VIPS_DEBUG_MSG_AMBER( "render_new: %p\n", render );
601 
602 #ifdef VIPS_DEBUG_AMBER
603 	render_num_renders += 1;
604 #endif /*VIPS_DEBUG_AMBER*/
605 
606 	return( render );
607 }
608 
609 /* Make a Tile.
610  */
611 static Tile *
tile_new(Render * render)612 tile_new( Render *render )
613 {
614 	Tile *tile;
615 
616 	VIPS_DEBUG_MSG_AMBER( "tile_new\n" );
617 
618 	/* Don't use auto-free: we need to make sure we free the tile after
619 	 * Render.
620 	 */
621 	if( !(tile = VIPS_NEW( NULL, Tile )) )
622 		return( NULL );
623 
624 	tile->render = render;
625 	tile->area.left = 0;
626 	tile->area.top = 0;
627 	tile->area.width = render->tile_width;
628 	tile->area.height = render->tile_height;
629 	tile->region = NULL;
630 	tile->painted = FALSE;
631 	tile->dirty = FALSE;
632 	tile->ticks = render->ticks;
633 
634 	if( !(tile->region = vips_region_new( render->in )) ) {
635 		(void) tile_free( tile, NULL, NULL );
636 		return( NULL );
637 	}
638 
639 	render->all = g_slist_prepend( render->all, tile );
640 	render->ntiles += 1;
641 
642 	return( tile );
643 }
644 
645 /* Search the cache for a tile by position.
646  */
647 static Tile *
render_tile_lookup(Render * render,VipsRect * area)648 render_tile_lookup( Render *render, VipsRect *area )
649 {
650 	return( (Tile *) g_hash_table_lookup( render->tiles, area ) );
651 }
652 
653 /* Add a new tile to the table.
654  */
655 static void
render_tile_add(Tile * tile,VipsRect * area)656 render_tile_add( Tile *tile, VipsRect *area )
657 {
658 	Render *render = tile->render;
659 
660 	g_assert( !render_tile_lookup( render, area ) );
661 
662 	tile->area = *area;
663 	tile->painted = FALSE;
664 
665 	/* Ignore buffer allocate errors, there's not much we could do with
666 	 * them.
667 	 */
668 	if( vips_region_buffer( tile->region, &tile->area ) )
669 		VIPS_DEBUG_MSG_RED( "render_tile_add: "
670 			"buffer allocate failed\n" );
671 
672 	g_hash_table_insert( render->tiles, &tile->area, tile );
673 }
674 
675 /* Move a tile to a new position.
676  */
677 static void
render_tile_move(Tile * tile,VipsRect * area)678 render_tile_move( Tile *tile, VipsRect *area )
679 {
680 	Render *render = tile->render;
681 
682 	g_assert( render_tile_lookup( render, &tile->area ) );
683 
684 	if( tile->area.left != area->left ||
685 		tile->area.top != area->top ) {
686 		g_assert( !render_tile_lookup( render, area ) );
687 
688 		g_hash_table_remove( render->tiles, &tile->area );
689 		render_tile_add( tile, area );
690 	}
691 }
692 
693 /* We've looked at a tile ... bump to end of LRU and front of dirty.
694  */
695 static void
tile_touch(Tile * tile)696 tile_touch( Tile *tile )
697 {
698 	Render *render = tile->render;
699 
700 	tile->ticks = render->ticks;
701 	render->ticks += 1;
702 	tile_dirty_bump( tile );
703 }
704 
705 /* Queue a tile for calculation.
706  */
707 static void
tile_queue(Tile * tile,VipsRegion * reg)708 tile_queue( Tile *tile, VipsRegion *reg )
709 {
710 	Render *render = tile->render;
711 
712 	VIPS_DEBUG_MSG( "tile_queue: adding tile %p %dx%d to dirty\n",
713 		tile, tile->area.left, tile->area.top );
714 
715 	tile->painted = FALSE;
716 	tile_touch( tile );
717 
718 	if( render->notify ) {
719 		/* Add to the list of renders with dirty tiles. The bg
720 		 * thread will pick it up and paint it. It can be already on
721 		 * the dirty list.
722 		 */
723 		tile_dirty_set( tile );
724 		render_dirty_put( render );
725 	}
726 	else {
727 		/* no notify ... paint the tile ourselves
728 		 * sychronously. No need to notify the client since they'll
729 		 * never see black tiles.
730 		 */
731 		VIPS_DEBUG_MSG( "tile_queue: "
732 			"painting tile %p %dx%d synchronously\n",
733 			tile, tile->area.left, tile->area.top );
734 
735 		/* While we're computing, let other threads use the cache.
736 		 * This tile won't get pulled out from under us since it's not
737 		 * marked as "painted", and it's not on the dirty list.
738 		 */
739 		g_mutex_unlock( render->lock );
740 
741 		if( vips_region_prepare_to( reg, tile->region,
742 			&tile->area, tile->area.left, tile->area.top ) )
743 			VIPS_DEBUG_MSG_RED( "tile_queue: prepare failed\n" );
744 
745 		g_mutex_lock( render->lock );
746 
747 		tile->painted = TRUE;
748 	}
749 }
750 
751 static void
tile_test_clean_ticks(VipsRect * key,Tile * value,Tile ** best)752 tile_test_clean_ticks( VipsRect *key, Tile *value, Tile **best )
753 {
754 	if( value->painted )
755 		if( !*best || value->ticks < (*best)->ticks )
756 			*best = value;
757 }
758 
759 /* Pick a painted tile to reuse. Search for LRU (slow!).
760  */
761 static Tile *
render_tile_get_painted(Render * render)762 render_tile_get_painted( Render *render )
763 {
764 	Tile *tile;
765 
766 	tile = NULL;
767 	g_hash_table_foreach( render->tiles,
768 		(GHFunc) tile_test_clean_ticks, &tile );
769 
770 	if( tile ) {
771 		VIPS_DEBUG_MSG( "render_tile_get_painted: "
772 			"reusing painted %p\n", tile );
773 	}
774 
775 	return( tile );
776 }
777 
778 /* Ask for an area of calculated pixels. Get from cache, request calculation,
779  * or if we've no threads or no notify, calculate immediately.
780  */
781 static Tile *
render_tile_request(Render * render,VipsRegion * reg,VipsRect * area)782 render_tile_request( Render *render, VipsRegion *reg, VipsRect *area )
783 {
784 	Tile *tile;
785 
786 	VIPS_DEBUG_MSG( "render_tile_request: asking for %dx%d\n",
787 		area->left, area->top );
788 
789 	if( (tile = render_tile_lookup( render, area )) ) {
790 		/* We already have a tile at this position. If it's invalid,
791 		 * ask for a repaint.
792 		 */
793 		if( tile->region->invalid )
794 			tile_queue( tile, reg );
795 		else
796 			tile_touch( tile );
797 	}
798 	else if( render->ntiles < render->max_tiles ||
799 		render->max_tiles == -1 ) {
800 		/* We have fewer tiles than teh max. We can just make a new
801 		 * tile.
802 		 */
803 		if( !(tile = tile_new( render )) )
804 			return( NULL );
805 
806 		render_tile_add( tile, area );
807 
808 		tile_queue( tile, reg );
809 	}
810 	else {
811 		/* Need to reuse a tile. Try for an old painted tile first,
812 		 * then if that fails, reuse a dirty tile.
813 		 */
814 		if( !(tile = render_tile_get_painted( render )) &&
815 			!(tile = render_tile_dirty_reuse( render )) ) {
816 			VIPS_DEBUG_MSG( "render_tile_request: "
817 				"no tiles to reuse\n" );
818 			return( NULL );
819 		}
820 
821 		render_tile_move( tile, area );
822 
823 		tile_queue( tile, reg );
824 	}
825 
826 	return( tile );
827 }
828 
829 /* Copy what we can from the tile into the region.
830  */
831 static void
tile_copy(Tile * tile,VipsRegion * to)832 tile_copy( Tile *tile, VipsRegion *to )
833 {
834 	VipsRect ovlap;
835 
836 	/* Find common pixels.
837 	 */
838 	vips_rect_intersectrect( &tile->area, &to->valid, &ovlap );
839 	g_assert( !vips_rect_isempty( &ovlap ) );
840 
841 	/* If the tile is painted, copy over the pixels. Otherwise, fill with
842 	 * zero.
843 	 */
844 	if( tile->painted && !tile->region->invalid ) {
845 		int len = VIPS_IMAGE_SIZEOF_PEL( to->im ) * ovlap.width;
846 
847 		int y;
848 
849 		VIPS_DEBUG_MSG( "tile_copy: "
850 			"copying calculated pixels for %p %dx%d\n",
851 			tile, tile->area.left, tile->area.top );
852 
853 		for( y = ovlap.top; y < VIPS_RECT_BOTTOM( &ovlap ); y++ ) {
854 			VipsPel *p = VIPS_REGION_ADDR( tile->region,
855 				ovlap.left, y );
856 			VipsPel *q = VIPS_REGION_ADDR( to, ovlap.left, y );
857 
858 			memcpy( q, p, len );
859 		}
860 	}
861 	else {
862 		VIPS_DEBUG_MSG( "tile_copy: zero filling for %p %dx%d\n",
863 			tile, tile->area.left, tile->area.top );
864 		vips_region_paint( to, &ovlap, 0 );
865 	}
866 }
867 
868 /* Loop over the output region, filling with data from cache.
869  */
870 static int
image_fill(VipsRegion * out,void * seq,void * a,void * b,gboolean * stop)871 image_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
872 {
873 	Render *render = (Render *) b;
874 	int tile_width = render->tile_width;
875 	int tile_height = render->tile_height;
876 	VipsRegion *reg = (VipsRegion *) seq;
877 	VipsRect *r = &out->valid;
878 
879 	int x, y;
880 
881 	/* Find top left of tiles we need.
882 	 */
883 	int xs = (r->left / tile_width) * tile_width;
884 	int ys = (r->top / tile_height) * tile_height;
885 
886 	VIPS_DEBUG_MSG( "image_fill: left = %d, top = %d, "
887 		"width = %d, height = %d\n",
888                 r->left, r->top, r->width, r->height );
889 
890 	g_mutex_lock( render->lock );
891 
892 	/*
893 
894 		FIXME ... if r fits inside a single tile, we could skip the
895 		copy.
896 
897 	 */
898 
899 	for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += tile_height )
900 		for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tile_width ) {
901 			VipsRect area;
902 			Tile *tile;
903 
904 			area.left = x;
905 			area.top = y;
906 			area.width = tile_width;
907 			area.height = tile_height;
908 
909 			tile = render_tile_request( render, reg, &area );
910 			if( tile )
911 				tile_copy( tile, out );
912 			else
913 				VIPS_DEBUG_MSG_RED( "image_fill: argh!\n" );
914 		}
915 
916 	g_mutex_unlock( render->lock );
917 
918 	return( 0 );
919 }
920 
921 /* The mask image is 255 / 0 for the state of painted for each tile.
922  */
923 static int
mask_fill(VipsRegion * out,void * seq,void * a,void * b,gboolean * stop)924 mask_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
925 {
926 	Render *render = (Render *) a;
927 	int tile_width = render->tile_width;
928 	int tile_height = render->tile_height;
929 	VipsRect *r = &out->valid;
930 
931 	int x, y;
932 
933 	/* Find top left of tiles we need.
934 	 */
935 	int xs = (r->left / tile_width) * tile_width;
936 	int ys = (r->top / tile_height) * tile_height;
937 
938 	VIPS_DEBUG_MSG( "mask_fill: left = %d, top = %d, "
939 		"width = %d, height = %d\n",
940                 r->left, r->top, r->width, r->height );
941 
942 	g_mutex_lock( render->lock );
943 
944 	for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += tile_height )
945 		for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tile_width ) {
946 			VipsRect area;
947 			Tile *tile;
948 			int value;
949 
950 			area.left = x;
951 			area.top = y;
952 			area.width = tile_width;
953 			area.height = tile_height;
954 
955 			tile = render_tile_lookup( render, &area );
956 			value = (tile &&
957                                 tile->painted &&
958 				!tile->region->invalid) ? 255 : 0;
959 
960 			/* Only mark painted tiles containing valid pixels.
961 			 */
962 			vips_region_paint( out, &area, value );
963 		}
964 
965 	g_mutex_unlock( render->lock );
966 
967 	return( 0 );
968 }
969 
970 /* Get the first render with dirty tiles.
971  */
972 static Render *
render_dirty_get(void)973 render_dirty_get( void )
974 {
975 	Render *render;
976 
977 	/* Wait for a render with dirty tiles.
978 	 */
979 	vips_semaphore_down( &n_render_dirty_sem );
980 
981 	g_mutex_lock( render_dirty_lock );
982 
983 	/* Just take the head of the jobs list ... we sort when we add.
984 	 */
985 	render = NULL;
986 	if( render_dirty_all ) {
987 		render = (Render *) render_dirty_all->data;
988 
989 		/* Ref the render to make sure it can't die while we're
990 		 * working on it.
991 		 */
992 		render_ref( render );
993 
994 		render_dirty_all = g_slist_remove( render_dirty_all, render );
995 	}
996 
997 	g_mutex_unlock( render_dirty_lock );
998 
999 	return( render );
1000 }
1001 
1002 /* Loop for the background render manager thread.
1003  */
1004 static void *
render_thread_main(void * client)1005 render_thread_main( void *client )
1006 {
1007 	Render *render;
1008 
1009 	while( !render_kill ) {
1010 		VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
1011 			"threadpool start\n" );
1012 
1013 		render_reschedule = FALSE;
1014 
1015 		if( (render = render_dirty_get()) ) {
1016 			if( vips_threadpool_run( render->in,
1017 				render_thread_state_new,
1018 				render_allocate,
1019 				render_work,
1020 				NULL,
1021 				render ) )
1022 				VIPS_DEBUG_MSG_RED( "render_thread_main: "
1023 					"threadpool_run failed\n" );
1024 
1025 			VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
1026 				"threadpool return\n" );
1027 
1028 			/* Add back to the jobs list, if we need to.
1029 			 */
1030 			render_dirty_put( render );
1031 
1032 			/* _get() does a ref to make sure we keep the render
1033 			 * alive during processing ... unref before we loop.
1034 			 * This can kill off the render.
1035 			 */
1036 			render_unref( render );
1037 		}
1038 	}
1039 
1040 	/* We are exiting, so render_thread must now be NULL.
1041 	 */
1042 	render_thread = NULL;
1043 
1044 	return( NULL );
1045 }
1046 
1047 static void *
vips__sink_screen_once(void * data)1048 vips__sink_screen_once( void *data )
1049 {
1050 	g_assert( !render_thread );
1051 	g_assert( !render_dirty_lock );
1052 
1053 	render_dirty_lock = vips_g_mutex_new();
1054 	vips_semaphore_init( &n_render_dirty_sem, 0, "n_render_dirty" );
1055 
1056 	/* Don't use vips__thread_execute() since this thread will only be
1057 	 * ended by vips_shutdown, and that isn't always called.
1058 	 */
1059 	render_thread = vips_g_thread_new( "sink_screen",
1060 		render_thread_main, NULL );
1061 
1062 	return( NULL );
1063 }
1064 
1065 /**
1066  * vips_sink_screen: (method)
1067  * @in: input image
1068  * @out: (out): output image
1069  * @mask: mask image indicating valid pixels
1070  * @tile_width: tile width
1071  * @tile_height: tile height
1072  * @max_tiles: maximum tiles to cache
1073  * @priority: rendering priority
1074  * @notify_fn: (scope call) (nullable): pixels are ready notification callback
1075  * @a: (closure notify_fn) (nullable): client data for callback
1076  *
1077  * This operation renders @in in the background, making pixels available on
1078  * @out as they are calculated. The @notify_fn callback is run every time a new
1079  * set of pixels are available. Calculated pixels are kept in a cache with
1080  * tiles sized @tile_width by @tile_height pixels and with at most @max_tiles
1081  * tiles.
1082  * If @max_tiles is -1, the cache is of unlimited size (up to the maximum image
1083  * size).
1084  * The @mask image is a one-band uchar image and has 255 for pixels which are
1085  * currently in cache and 0 for uncalculated pixels.
1086  *
1087  * Only a single sink is calculated at any one time, though many may be
1088  * alive. Use @priority to indicate which renders are more important:
1089  * zero means normal
1090  * priority, negative numbers are low priority, positive numbers high
1091  * priority.
1092  *
1093  * Calls to vips_region_prepare() on @out return immediately and hold
1094  * whatever is
1095  * currently in cache for that #VipsRect (check @mask to see which parts of the
1096  * #VipsRect are valid). Any pixels in the #VipsRect which are not in
1097  * cache are added
1098  * to a queue, and the @notify_fn callback will trigger when those pixels are
1099  * ready.
1100  *
1101  * The @notify_fn callback is run from one of the background threads. In the
1102  * callback
1103  * you need to somehow send a message to the main thread that the pixels are
1104  * ready. In a glib-based application, this is easily done with g_idle_add().
1105  *
1106  * If @notify_fn is %NULL then vips_sink_screen() runs synchronously.
1107  * vips_region_prepare() on @out will always block until the pixels have been
1108  * calculated.
1109  *
1110  * See also: vips_tilecache(), vips_region_prepare(),
1111  * vips_sink_disc(), vips_sink().
1112  *
1113  * Returns: 0 on sucess, -1 on error.
1114  */
1115 int
vips_sink_screen(VipsImage * in,VipsImage * out,VipsImage * mask,int tile_width,int tile_height,int max_tiles,int priority,VipsSinkNotify notify_fn,void * a)1116 vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask,
1117 	int tile_width, int tile_height,
1118 	int max_tiles,
1119 	int priority,
1120 	VipsSinkNotify notify_fn, void *a )
1121 {
1122 	static GOnce once = G_ONCE_INIT;
1123 
1124 	Render *render;
1125 
1126 	VIPS_ONCE( &once, vips__sink_screen_once, NULL );
1127 
1128 	if( tile_width <= 0 || tile_height <= 0 ||
1129 		max_tiles < -1 ) {
1130 		vips_error( "vips_sink_screen", "%s", _( "bad parameters" ) );
1131 		return( -1 );
1132 	}
1133 
1134 	if( vips_image_pio_input( in ) ||
1135 		vips_image_pipelinev( out,
1136 			VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
1137 		return( -1 );
1138 
1139 	if( mask ) {
1140 		if( vips_image_pipelinev( mask,
1141 			VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
1142 			return( -1 );
1143 
1144 		mask->Bands = 1;
1145 		mask->BandFmt = VIPS_FORMAT_UCHAR;
1146 		mask->Type = VIPS_INTERPRETATION_B_W;
1147 		mask->Coding = VIPS_CODING_NONE;
1148 	}
1149 
1150 	if( !(render = render_new( in, out, mask,
1151 		tile_width, tile_height, max_tiles, priority, notify_fn, a )) )
1152 		return( -1 );
1153 
1154 	VIPS_DEBUG_MSG( "vips_sink_screen: max = %d, %p\n", max_tiles, render );
1155 
1156 	if( vips_image_generate( out,
1157 		vips_start_one, image_fill, vips_stop_one, in, render ) )
1158 		return( -1 );
1159 	if( mask &&
1160 		vips_image_generate( mask,
1161 			NULL, mask_fill, NULL, render, NULL ) )
1162 		return( -1 );
1163 
1164 	return( 0 );
1165 }
1166 
1167 int
vips__print_renders(void)1168 vips__print_renders( void )
1169 {
1170 	int n_leaks;
1171 
1172 	n_leaks = 0;
1173 
1174 #ifdef VIPS_DEBUG_AMBER
1175 	if( render_num_renders > 0 ) {
1176 		printf( "%d active renders\n", render_num_renders );
1177 		n_leaks += render_num_renders;
1178 	}
1179 #endif /*VIPS_DEBUG_AMBER*/
1180 
1181 	if( render_dirty_lock ) {
1182 		g_mutex_lock( render_dirty_lock );
1183 
1184 		n_leaks += g_slist_length( render_dirty_all );
1185 		if( render_dirty_all )
1186 			printf( "dirty renders\n" );
1187 
1188 		g_mutex_unlock( render_dirty_lock );
1189 	}
1190 
1191 	return( n_leaks );
1192 }
1193