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