1 /* helper functions for Orc
2  *
3  * 29/10/10
4  * 	- from morph hacking
5  */
6 
7 /*
8 
9     This file is part of VIPS.
10 
11     VIPS is free software; you can redistribute it and/or modify
12     it under the terms of the GNU Lesser General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU Lesser General Public License for more details.
20 
21     You should have received a copy of the GNU Lesser General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24     02110-1301  USA
25 
26  */
27 
28 /*
29 
30     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31 
32  */
33 
34 /*
35 
36  	TODO
37 
38 	- would setting params by index rather than name be any quicker?
39 
40  */
41 
42 /* Verbose messages from Orc (or use ORC_DEBUG=99 on the command-line).
43 #define DEBUG_ORC
44  */
45 
46 /*
47 #define DEBUG
48  */
49 
50 /* Trace all orc calls, handy for debugging.
51 #define DEBUG_TRACE
52  */
53 
54 #ifdef HAVE_CONFIG_H
55 #include <config.h>
56 #endif /*HAVE_CONFIG_H*/
57 
58 #include <vips/intl.h>
59 
60 #include <stdlib.h>
61 
62 #include <vips/vips.h>
63 #include <vips/vector.h>
64 #include <vips/internal.h>
65 #include <vips/thread.h>
66 
67 /* Cleared by the command-line --vips-novector switch and the VIPS_NOVECTOR env
68  * var.
69  */
70 gboolean vips__vector_enabled = TRUE;
71 
72 void
vips_vector_error(VipsVector * vector)73 vips_vector_error( VipsVector *vector )
74 {
75 #ifdef HAVE_ORC
76 #ifdef HAVE_ORC_PROGRAM_GET_ERROR
77 	if( vector->program )
78 		g_warning( "orc error: %s",
79 			orc_program_get_error( vector->program ) );
80 #endif /*HAVE_ORC_PROGRAM_GET_ERROR*/
81 #endif /*HAVE_ORC*/
82 }
83 
84 void
vips_vector_init(void)85 vips_vector_init( void )
86 {
87 #ifdef HAVE_ORC
88 #ifdef DEBUG_TRACE
89 	printf( "orc_init();\n" );
90 #endif /*DEBUG_TRACE*/
91 	orc_init();
92 
93 #ifdef DEBUG_ORC
94 	/* You can also do ORC_DEBUG=99 at the command-line.
95 	 */
96 #ifdef DEBUG_TRACE
97 	printf( "orc_debug_set_level( 99 );\n" );
98 #endif /*DEBUG_TRACE*/
99 	orc_debug_set_level( 99 );
100 #endif /*DEBUG_ORC*/
101 
102 	/* Look for the environment variable VIPS_NOVECTOR and use that to turn
103 	 * off as well.
104 	 */
105 	if( g_getenv( "VIPS_NOVECTOR" )
106 #if ENABLE_DEPRECATED
107 		|| g_getenv( "IM_NOVECTOR" )
108 #endif
109 	)
110 		vips__vector_enabled = FALSE;
111 
112 #endif /*HAVE_ORC*/
113 }
114 
115 gboolean
vips_vector_isenabled(void)116 vips_vector_isenabled( void )
117 {
118 #ifdef HAVE_ORC
119 	return( vips__vector_enabled );
120 #else /*!HAVE_ORC*/
121 	return( FALSE );
122 #endif /*HAVE_ORC*/
123 }
124 
125 void
vips_vector_set_enabled(gboolean enabled)126 vips_vector_set_enabled( gboolean enabled )
127 {
128 	vips__vector_enabled = enabled;
129 }
130 
131 void
vips_vector_free(VipsVector * vector)132 vips_vector_free( VipsVector *vector )
133 {
134 #ifdef HAVE_ORC
135 	/* orc-0.4.19 will crash if you free programs. Update your orc, or
136 	 * comment out this line.
137 	 *
138 	 * See https://bugzilla.gnome.org/show_bug.cgi?id=731227
139 	 *
140 	 * orc does not set any version variables so we can't disable this
141 	 * free automatically.
142 	 */
143 #ifdef DEBUG_TRACE
144 	printf( "orc_program_free( %s );\n", vector->unique_name );
145 	printf( "%s = NULL;\n", vector->unique_name );
146 #endif /*DEBUG_TRACE*/
147 	VIPS_FREEF( orc_program_free, vector->program );
148 #endif /*HAVE_ORC*/
149 	VIPS_FREE( vector->unique_name );
150 	VIPS_FREE( vector );
151 }
152 
153 VipsVector *
vips_vector_new(const char * name,int dsize)154 vips_vector_new( const char *name, int dsize )
155 {
156 	static int vector_number = 0;
157 
158 	VipsVector *vector;
159 	int i;
160 
161 	if( !(vector = VIPS_NEW( NULL, VipsVector )) )
162 		return( NULL );
163 	vector->name = name;
164 	vector->unique_name = g_strdup_printf( "p[%d]", vector_number++ );
165 	vector->n_temp = 0;
166 	vector->n_scanline = 0;
167 	vector->n_source = 0;
168 	vector->n_destination = 0;
169 	vector->n_constant = 0;
170 	vector->n_parameter = 0;
171 	vector->n_instruction = 0;
172 
173 	for( i = 0; i < VIPS_VECTOR_SOURCE_MAX; i++ ) {
174 		vector->s[i] = -1;
175 		vector->sl[i] = -1;
176 	}
177 
178 	vector->d1 = -1;
179 
180 	vector->compiled = FALSE;
181 
182 #ifdef HAVE_ORC
183 	vector->program = orc_program_new();
184 #ifdef DEBUG_TRACE
185 	printf( "%s = orc_program_new();\n", vector->unique_name );
186 #endif /*DEBUG_TRACE*/
187 #endif /*HAVE_ORC*/
188 
189 	/* We always make d1, our callers make either a single point source, or
190 	 * for area ops, a set of scanlines.
191 	 *
192 	 * Don't check error return. orc uses 0 to mean error, but the first
193 	 * var you create will have id 0 :-( The first var is unlikely to fail
194 	 * anyway.
195 	 */
196 	vector->d1 = vips_vector_destination( vector, "d1", dsize );
197 
198 	return( vector );
199 }
200 
201 void
vips_vector_asm2(VipsVector * vector,const char * op,const char * a,const char * b)202 vips_vector_asm2( VipsVector *vector,
203 	const char *op, const char *a, const char *b )
204 {
205 	vector->n_instruction += 1;
206 
207 #ifdef DEBUG
208 	 printf( "  %s %s %s\n", op, a, b );
209 #endif /*DEBUG*/
210 
211 #ifdef HAVE_ORC
212 #ifdef DEBUG_TRACE
213 	printf( "orc_program_append_ds_str( %s, \"%s\", \"%s\", \"%s\" );\n",
214 		vector->unique_name, op, a, b );
215 #endif /*DEBUG_TRACE*/
216 	 orc_program_append_ds_str( vector->program, op, a, b );
217 #endif /*HAVE_ORC*/
218 }
219 
220 void
vips_vector_asm3(VipsVector * vector,const char * op,const char * a,const char * b,const char * c)221 vips_vector_asm3( VipsVector *vector,
222 	const char *op, const char *a, const char *b, const char *c )
223 {
224 	vector->n_instruction += 1;
225 
226 #ifdef DEBUG
227 	 printf( "  %s %s %s %s\n", op, a, b, c );
228 #endif /*DEBUG*/
229 
230 #ifdef HAVE_ORC
231 #ifdef DEBUG_TRACE
232 	printf( "orc_program_append_str( %s, \"%s\", "
233 		"\"%s\", \"%s\", \"%s\" );\n",
234 		vector->unique_name, op, a, b, c );
235 #endif /*DEBUG_TRACE*/
236 	 orc_program_append_str( vector->program, op, a, b, c );
237 #endif /*HAVE_ORC*/
238 }
239 
240 void
vips_vector_constant(VipsVector * vector,char * name,int value,int size)241 vips_vector_constant( VipsVector *vector, char *name, int value, int size )
242 {
243 #ifdef HAVE_ORC
244 	char *sname;
245 
246 	if( size == 1 )
247 		sname = "b";
248 	else if( size == 2 )
249 		sname = "w";
250 	else if( size == 4 )
251 		sname = "l";
252 	else {
253 		printf( "vips_vector_constant: bad constant size\n" );
254 
255 		/* Not really correct, heh.
256 		 */
257 		sname = "x";
258 	}
259 
260 	if( value > 0 )
261 		vips_snprintf( name, 256, "c%d%s", value, sname );
262 	else
263 		vips_snprintf( name, 256, "cm%d%s", -value, sname );
264 
265 	if( orc_program_find_var_by_name( vector->program, name ) == -1 ) {
266 #ifdef DEBUG_TRACE
267 		printf( "orc_program_add_constant( %s, %d, %d, \"%s\" );\n",
268 			vector->unique_name, size, value, name );
269 #endif /*DEBUG_TRACE*/
270 		if( !orc_program_add_constant( vector->program,
271 			size, value, name ) )
272 			vips_vector_error( vector );
273 		vector->n_constant += 1;
274 	}
275 #endif /*HAVE_ORC*/
276 }
277 
278 void
vips_vector_source_scanline(VipsVector * vector,char * name,int line,int size)279 vips_vector_source_scanline( VipsVector *vector,
280 	char *name, int line, int size )
281 {
282 #ifdef HAVE_ORC
283 	vips_snprintf( name, 256, "sl%d", line );
284 
285 	if( orc_program_find_var_by_name( vector->program, name ) == -1 ) {
286 		int var;
287 
288 		if( !(var = orc_program_add_source( vector->program,
289 			size, name )) )
290 			vips_vector_error( vector );
291 #ifdef DEBUG_TRACE
292 		printf( "orc_program_add_source( %s, %d, \"%s\" );\n",
293 			vector->unique_name, size, name );
294 #endif /*DEBUG_TRACE*/
295 		vector->sl[vector->n_scanline] = var;
296 		vector->line[vector->n_scanline] = line;
297 		vector->n_scanline += 1;
298 	}
299 #endif /*HAVE_ORC*/
300 }
301 
302 int
vips_vector_source_name(VipsVector * vector,const char * name,int size)303 vips_vector_source_name( VipsVector *vector, const char *name, int size )
304 {
305 	int var;
306 
307 #ifdef HAVE_ORC
308 	g_assert( orc_program_find_var_by_name( vector->program, name ) == -1 );
309 
310 	if( !(var = orc_program_add_source( vector->program, size, name )) )
311 		vips_vector_error( vector );
312 	vector->s[vector->n_source] = var;
313 #ifdef DEBUG_TRACE
314 	printf( "orc_program_add_source( %s, %d, \"%s\" );\n",
315 		vector->unique_name, size, name );
316 #endif /*DEBUG_TRACE*/
317 	vector->n_source += 1;
318 #else /*!HAVE_ORC*/
319 	var = -1;
320 #endif /*HAVE_ORC*/
321 
322 	return( var );
323 }
324 
325 void
vips_vector_temporary(VipsVector * vector,const char * name,int size)326 vips_vector_temporary( VipsVector *vector, const char *name, int size )
327 {
328 #ifdef HAVE_ORC
329 	g_assert( orc_program_find_var_by_name( vector->program, name ) == -1 );
330 
331 	if( !orc_program_add_temporary( vector->program, size, name ) )
332 		vips_vector_error( vector );
333 
334 #ifdef DEBUG_TRACE
335 	printf( "orc_program_add_temporary( %s, %d, \"%s\" );\n",
336 		vector->unique_name, size, name );
337 #endif /*DEBUG_TRACE*/
338 	vector->n_temp += 1;
339 #endif /*HAVE_ORC*/
340 }
341 
342 int
vips_vector_parameter(VipsVector * vector,const char * name,int size)343 vips_vector_parameter( VipsVector *vector, const char *name, int size )
344 {
345 	int var;
346 
347 #ifdef HAVE_ORC
348 	g_assert( orc_program_find_var_by_name( vector->program, name ) == -1 );
349 
350 	var = orc_program_add_parameter( vector->program, size, name );
351 	if( !var )
352 		vips_vector_error( vector );
353 
354 #ifdef DEBUG_TRACE
355 	printf( "orc_program_add_parameter( %s, %d, \"%s\" );\n",
356 		vector->unique_name, size, name );
357 #endif /*DEBUG_TRACE*/
358 	vector->n_parameter += 1;
359 #else /*!HAVE_ORC*/
360 	var = -1;
361 #endif /*HAVE_ORC*/
362 
363 	return ( var );
364 }
365 
366 int
vips_vector_destination(VipsVector * vector,const char * name,int size)367 vips_vector_destination( VipsVector *vector, const char *name, int size )
368 {
369 	int var;
370 
371 #ifdef HAVE_ORC
372 	g_assert( orc_program_find_var_by_name( vector->program, name ) == -1 );
373 
374 	var = orc_program_add_destination( vector->program, size, name );
375 #ifdef DEBUG_TRACE
376 	printf( "orc_program_add_destination( %d, \"%s\" );\n",
377 		size, name );
378 #endif /*DEBUG_TRACE*/
379 	vector->n_destination += 1;
380 #else /*!HAVE_ORC*/
381 	var = -1;
382 #endif /*HAVE_ORC*/
383 
384 	return( var );
385 }
386 
387 gboolean
vips_vector_full(VipsVector * vector)388 vips_vector_full( VipsVector *vector )
389 {
390 	/* Many orcs don't have ORC_MAX_CONST_VARS etc., stick to our own
391 	 * constants for now.
392 	 */
393 
394 	/* We can need a max of 2 constants plus one source per
395 	 * coefficient, so stop if we're sure we don't have enough.
396 	 */
397 	if( vector->n_constant + 2 > 8 )
398 		return( TRUE );
399 
400 	/* You can have 8 source, and d1 counts as one of them, so +1
401 	 * there.
402 	 */
403 	if( vector->n_source + vector->n_scanline + 1 > 7 )
404 		return( TRUE );
405 
406 	/* Need to leave some space, so 1 spare.
407 	 */
408 	if( vector->n_parameter > 7 )
409 		return( TRUE );
410 
411 	/* After signalling full, some operations will add up to 4 more
412 	 * instructions as they finish up. Leave a margin.
413 	 */
414 	if( vector->n_instruction + 10 > 50 )
415 		return( TRUE );
416 
417 	return( FALSE );
418 }
419 
420 gboolean
vips_vector_compile(VipsVector * vector)421 vips_vector_compile( VipsVector *vector )
422 {
423 #ifdef HAVE_ORC
424 	OrcCompileResult result;
425 
426 	/* Some orcs seem to be unstable with many compilers active at once.
427 	 */
428 	g_mutex_lock( vips__global_lock );
429 	result = orc_program_compile( vector->program );
430 	g_mutex_unlock( vips__global_lock );
431 
432 #ifdef DEBUG_TRACE
433 	printf( "orc_program_compile( %s );\n", vector->unique_name );
434 #endif /*DEBUG_TRACE*/
435 	if( !ORC_COMPILE_RESULT_IS_SUCCESSFUL( result ) ) {
436 #ifdef DEBUG
437 		printf( "*** error compiling %s\n", vector->name );
438 #endif /*DEBUG*/
439 
440 		return( FALSE );
441 	}
442 
443 	vector->compiled = TRUE;
444 #endif /*HAVE_ORC*/
445 
446 	return( TRUE );
447 }
448 
449 void
vips_vector_print(VipsVector * vector)450 vips_vector_print( VipsVector *vector )
451 {
452 	int i;
453 
454 	printf( "%s: ", vector->name );
455 	if( vector->compiled )
456 		printf( "successfully compiled\n" );
457 	else
458 		printf( "not compiled\n" );
459 	printf( "  n_scanline = %d\n", vector->n_scanline );
460 	for( i = 0; i < vector->n_scanline; i++ )
461 		printf( "        var %d = line %d\n",
462 			vector->sl[i], vector->line[i] );
463 	printf( "  n_source = %d\n", vector->n_source );
464 	for( i = 0; i < vector->n_source; i++ )
465 		printf( "        var %d\n", vector->s[i] );
466 	printf( "  n_parameter = %d\n", vector->n_parameter );
467 	printf( "  n_destination = %d\n", vector->n_destination );
468 	printf( "  n_constant = %d\n", vector->n_constant );
469 	printf( "  n_temp = %d\n", vector->n_temp );
470 	printf( "  n_instruction = %d\n", vector->n_instruction );
471 }
472 
473 void
vips_executor_set_program(VipsExecutor * executor,VipsVector * vector,int n)474 vips_executor_set_program( VipsExecutor *executor, VipsVector *vector, int n )
475 {
476 #ifdef HAVE_ORC
477 	executor->vector = vector;
478 
479 	orc_executor_set_program( &executor->executor, vector->program );
480 	orc_executor_set_n( &executor->executor, n );
481 #endif /*HAVE_ORC*/
482 }
483 
484 void
vips_executor_set_array(VipsExecutor * executor,int var,void * value)485 vips_executor_set_array( VipsExecutor *executor, int var, void *value )
486 {
487 #ifdef HAVE_ORC
488 	if( var != -1 )
489 		orc_executor_set_array( &executor->executor, var, value );
490 #endif /*HAVE_ORC*/
491 }
492 
493 void
vips_executor_set_parameter(VipsExecutor * executor,int var,int value)494 vips_executor_set_parameter( VipsExecutor *executor, int var, int value )
495 {
496 #ifdef HAVE_ORC
497 	if( var != -1 )
498 		orc_executor_set_param( &executor->executor, var, value );
499 #endif /*HAVE_ORC*/
500 }
501 
502 void
vips_executor_set_scanline(VipsExecutor * executor,VipsRegion * ir,int x,int y)503 vips_executor_set_scanline( VipsExecutor *executor,
504 	VipsRegion *ir, int x, int y )
505 {
506 	VipsVector *vector = executor->vector;
507 	VipsPel *base = VIPS_REGION_ADDR( ir, x, y );
508 	int lsk = VIPS_REGION_LSKIP( ir );
509 
510 	int i;
511 
512 	for( i = 0; i < vector->n_scanline; i++ )
513 		vips_executor_set_array( executor,
514 			vector->sl[i], base + vector->line[i] * lsk );
515 }
516 
517 void
vips_executor_set_destination(VipsExecutor * executor,void * value)518 vips_executor_set_destination( VipsExecutor *executor, void *value )
519 {
520 	VipsVector *vector = executor->vector;
521 
522 	vips_executor_set_array( executor, vector->d1, value );
523 }
524 
525 void
vips_executor_run(VipsExecutor * executor)526 vips_executor_run( VipsExecutor *executor )
527 {
528 #ifdef HAVE_ORC
529 	orc_executor_run( &executor->executor );
530 #endif /*HAVE_ORC*/
531 }
532 
533 /* Make a fixed-point version of a matrix. Each
534  * out[i] = rint(in[i] * adj_scale), where adj_scale is selected so that
535  * sum(out) = sum(in) * scale.
536  *
537  * Because of the vagaries of rint(), we can't just calc this, we have to
538  * iterate and converge on the best value for adj_scale.
539  */
540 void
vips_vector_to_fixed_point(double * in,int * out,int n,int scale)541 vips_vector_to_fixed_point( double *in, int *out, int n, int scale )
542 {
543 	double fsum;
544 	int i;
545 	int target;
546 	int sum;
547 	double high;
548 	double low;
549 	double guess;
550 
551 	fsum = 0.0;
552 	for( i = 0; i < n; i++ )
553 		fsum += in[i];
554 	target = VIPS_RINT( fsum * scale );
555 
556 	/* As we rint() each scale element, we can get up to 0.5 error.
557 	 * Therefore, by the end of the mask, we can be off by up to n/2. Our
558 	 * high and low guesses are therefore n/2 either side of the obvious
559 	 * answer.
560 	 */
561 	high = scale + (n + 1) / 2;
562 	low = scale - (n + 1) / 2;
563 
564 	do {
565 		guess = (high + low) / 2.0;
566 
567 		for( i = 0; i < n; i++ )
568 			out[i] = VIPS_RINT( in[i] * guess );
569 
570 		sum = 0;
571 		for( i = 0; i < n; i++ )
572 			sum += out[i];
573 
574 		if( sum == target )
575 			break;
576 		if( sum < target )
577 			low = guess;
578 		if( sum > target )
579 			high = guess;
580 
581 	/* This will typically produce about 5 iterations.
582 	 */
583 	} while( high - low > 0.01 );
584 
585 	if( sum != target ) {
586 		/* Spread the error out thinly over the whole array. For
587 		 * example, consider the matrix:
588 		 *
589 		 * 	3 3 9 0
590 		 *	1 1 1
591 		 *	1 1 1
592 		 *	1 1 1
593 		 *
594 		 * being converted with scale = 64 (convi does this). We want
595 		 * to generate a mix of 7s and 8s.
596 		 */
597 		int each_error = (target - sum) / n;
598 		int extra_error = (target - sum) % n;
599 
600 		/* To share the residual error, we add or subtract 1 from the
601 		 * first abs(extra_error) elements.
602 		 */
603 		int direction = extra_error > 0 ? 1 : -1;
604 		int n_elements = VIPS_ABS( extra_error );
605 
606 		for( i = 0; i < n; i++ )
607 			out[i] += each_error;
608 
609 		for( i = 0; i < n_elements; i++ )
610 			out[i] += direction;
611 	}
612 }
613