dct-test: remove 'ref' function pointer from tables
[ffmpeg.git] / libavcodec / dct-test.c
1 /*
2  * (c) 2001 Fabrice Bellard
3  *     2007 Marc Hoffman <marc.hoffman@analog.com>
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * DCT test (c) 2001 Fabrice Bellard
25  * Started from sample code by Juan J. Sierralta P.
26  */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/time.h>
32 #include <unistd.h>
33 #include <math.h>
34
35 #include "libavutil/cpu.h"
36 #include "libavutil/common.h"
37 #include "libavutil/lfg.h"
38
39 #include "simple_idct.h"
40 #include "aandcttab.h"
41 #include "faandct.h"
42 #include "faanidct.h"
43 #include "x86/idct_xvid.h"
44 #include "dctref.h"
45
46 #undef printf
47
48 void ff_mmx_idct(DCTELEM *data);
49 void ff_mmxext_idct(DCTELEM *data);
50
51 void odivx_idct_c(short *block);
52
53 // BFIN
54 void ff_bfin_idct(DCTELEM *block);
55 void ff_bfin_fdct(DCTELEM *block);
56
57 // ALTIVEC
58 void fdct_altivec(DCTELEM *block);
59 //void idct_altivec(DCTELEM *block);?? no routine
60
61 // ARM
62 void ff_j_rev_dct_arm(DCTELEM *data);
63 void ff_simple_idct_arm(DCTELEM *data);
64 void ff_simple_idct_armv5te(DCTELEM *data);
65 void ff_simple_idct_armv6(DCTELEM *data);
66 void ff_simple_idct_neon(DCTELEM *data);
67
68 void ff_simple_idct_axp(DCTELEM *data);
69
70 struct algo {
71     const char *name;
72     void (*func)(DCTELEM *block);
73     enum formattag { NO_PERM, MMX_PERM, MMX_SIMPLE_PERM, SCALE_PERM,
74                      SSE2_PERM, PARTTRANS_PERM } format;
75     int mm_support;
76     int nonspec;
77 };
78
79 #ifndef FAAN_POSTSCALE
80 #define FAAN_SCALE SCALE_PERM
81 #else
82 #define FAAN_SCALE NO_PERM
83 #endif
84
85 static int cpu_flags;
86
87 static const struct algo fdct_tab[] = {
88     { "REF-DBL",        ff_ref_fdct,           NO_PERM    },
89     { "FAAN",           ff_faandct,            FAAN_SCALE },
90     { "IJG-AAN-INT",    fdct_ifast,            SCALE_PERM },
91     { "IJG-LLM-INT",    ff_jpeg_fdct_islow,    NO_PERM    },
92
93 #if HAVE_MMX
94     { "MMX",            ff_fdct_mmx,           NO_PERM,   AV_CPU_FLAG_MMX     },
95     { "MMX2",           ff_fdct_mmx2,          NO_PERM,   AV_CPU_FLAG_MMX2    },
96     { "SSE2",           ff_fdct_sse2,          NO_PERM,   AV_CPU_FLAG_SSE2    },
97 #endif
98
99 #if HAVE_ALTIVEC
100     { "altivecfdct",    fdct_altivec,          NO_PERM,   AV_CPU_FLAG_ALTIVEC },
101 #endif
102
103 #if ARCH_BFIN
104     { "BFINfdct",       ff_bfin_fdct,          NO_PERM  },
105 #endif
106
107     { 0 }
108 };
109
110 static const struct algo idct_tab[] = {
111     { "FAANI",          ff_faanidct,           NO_PERM  },
112     { "REF-DBL",        ff_ref_idct,           NO_PERM  },
113     { "INT",            j_rev_dct,             MMX_PERM },
114     { "SIMPLE-C",       ff_simple_idct,        NO_PERM  },
115
116 #if HAVE_MMX
117 #if CONFIG_GPL
118     { "LIBMPEG2-MMX",   ff_mmx_idct,           MMX_PERM,  AV_CPU_FLAG_MMX,  1 },
119     { "LIBMPEG2-MMX2",  ff_mmxext_idct,        MMX_PERM,  AV_CPU_FLAG_MMX2, 1 },
120 #endif
121     { "SIMPLE-MMX",     ff_simple_idct_mmx,  MMX_SIMPLE_PERM, AV_CPU_FLAG_MMX },
122     { "XVID-MMX",       ff_idct_xvid_mmx,      NO_PERM,   AV_CPU_FLAG_MMX,  1 },
123     { "XVID-MMX2",      ff_idct_xvid_mmx2,     NO_PERM,   AV_CPU_FLAG_MMX2, 1 },
124     { "XVID-SSE2",      ff_idct_xvid_sse2,     SSE2_PERM, AV_CPU_FLAG_SSE2, 1 },
125 #endif
126
127 #if ARCH_BFIN
128     { "BFINidct",       ff_bfin_idct,          NO_PERM  },
129 #endif
130
131 #if ARCH_ARM
132     { "SIMPLE-ARM",     ff_simple_idct_arm,    NO_PERM  },
133     { "INT-ARM",        ff_j_rev_dct_arm,      MMX_PERM },
134 #endif
135 #if HAVE_ARMV5TE
136     { "SIMPLE-ARMV5TE", ff_simple_idct_armv5te,NO_PERM  },
137 #endif
138 #if HAVE_ARMV6
139     { "SIMPLE-ARMV6",   ff_simple_idct_armv6,  MMX_PERM },
140 #endif
141 #if HAVE_NEON
142     { "SIMPLE-NEON",    ff_simple_idct_neon,   PARTTRANS_PERM },
143 #endif
144
145 #if ARCH_ALPHA
146     { "SIMPLE-ALPHA",   ff_simple_idct_axp,    NO_PERM },
147 #endif
148
149     { 0 }
150 };
151
152 #define AANSCALE_BITS 12
153
154 uint8_t cropTbl[256 + 2 * MAX_NEG_CROP];
155
156 static int64_t gettime(void)
157 {
158     struct timeval tv;
159     gettimeofday(&tv, NULL);
160     return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
161 }
162
163 #define NB_ITS 20000
164 #define NB_ITS_SPEED 50000
165
166 static short idct_mmx_perm[64];
167
168 static short idct_simple_mmx_perm[64] = {
169     0x00, 0x08, 0x04, 0x09, 0x01, 0x0C, 0x05, 0x0D,
170     0x10, 0x18, 0x14, 0x19, 0x11, 0x1C, 0x15, 0x1D,
171     0x20, 0x28, 0x24, 0x29, 0x21, 0x2C, 0x25, 0x2D,
172     0x12, 0x1A, 0x16, 0x1B, 0x13, 0x1E, 0x17, 0x1F,
173     0x02, 0x0A, 0x06, 0x0B, 0x03, 0x0E, 0x07, 0x0F,
174     0x30, 0x38, 0x34, 0x39, 0x31, 0x3C, 0x35, 0x3D,
175     0x22, 0x2A, 0x26, 0x2B, 0x23, 0x2E, 0x27, 0x2F,
176     0x32, 0x3A, 0x36, 0x3B, 0x33, 0x3E, 0x37, 0x3F,
177 };
178
179 static const uint8_t idct_sse2_row_perm[8] = { 0, 4, 1, 5, 2, 6, 3, 7 };
180
181 static void idct_mmx_init(void)
182 {
183     int i;
184
185     /* the mmx/mmxext idct uses a reordered input, so we patch scan tables */
186     for (i = 0; i < 64; i++) {
187         idct_mmx_perm[i] = (i & 0x38) | ((i & 6) >> 1) | ((i & 1) << 2);
188     }
189 }
190
191 DECLARE_ALIGNED(16, static DCTELEM, block)[64];
192 DECLARE_ALIGNED(8,  static DCTELEM, block1)[64];
193 DECLARE_ALIGNED(8,  static DCTELEM, block_org)[64];
194
195 static inline void mmx_emms(void)
196 {
197 #if HAVE_MMX
198     if (cpu_flags & AV_CPU_FLAG_MMX)
199         __asm__ volatile ("emms\n\t");
200 #endif
201 }
202
203 static int dct_error(const struct algo *dct, int test, int is_idct, int speed)
204 {
205     void (*ref)(DCTELEM *block) = is_idct ? ff_ref_idct : ff_ref_fdct;
206     int it, i, scale;
207     int err_inf, v;
208     int64_t err2, ti, ti1, it1, err_sum = 0;
209     int64_t sysErr[64], sysErrMax = 0;
210     int maxout = 0;
211     int blockSumErrMax = 0, blockSumErr;
212     AVLFG prng;
213     double omse, ome;
214     int spec_err;
215
216     av_lfg_init(&prng, 1);
217
218     err_inf = 0;
219     err2 = 0;
220     for (i = 0; i < 64; i++)
221         sysErr[i] = 0;
222     for (it = 0; it < NB_ITS; it++) {
223         for (i = 0; i < 64; i++)
224             block1[i] = 0;
225         switch (test) {
226         case 0:
227             for (i = 0; i < 64; i++)
228                 block1[i] = (av_lfg_get(&prng) % 512) - 256;
229             if (is_idct) {
230                 ff_ref_fdct(block1);
231                 for (i = 0; i < 64; i++)
232                     block1[i] >>= 3;
233             }
234             break;
235         case 1: {
236                 int num = av_lfg_get(&prng) % 10 + 1;
237                 for (i = 0; i < num; i++)
238                     block1[av_lfg_get(&prng) % 64] =
239                         av_lfg_get(&prng) % 512 - 256;
240             }
241             break;
242         case 2:
243             block1[0] = av_lfg_get(&prng) % 4096 - 2048;
244             block1[63] = (block1[0] & 1) ^ 1;
245             break;
246         }
247
248         for (i = 0; i < 64; i++)
249             block_org[i] = block1[i];
250
251         if (dct->format == MMX_PERM) {
252             for (i = 0; i < 64; i++)
253                 block[idct_mmx_perm[i]] = block1[i];
254         } else if (dct->format == MMX_SIMPLE_PERM) {
255             for (i = 0; i < 64; i++)
256                 block[idct_simple_mmx_perm[i]] = block1[i];
257         } else if (dct->format == SSE2_PERM) {
258             for (i = 0; i < 64; i++)
259                 block[(i & 0x38) | idct_sse2_row_perm[i & 7]] = block1[i];
260         } else if (dct->format == PARTTRANS_PERM) {
261             for (i = 0; i < 64; i++)
262                 block[(i & 0x24) | ((i & 3) << 3) | ((i >> 3) & 3)] = block1[i];
263         } else {
264             for (i = 0; i < 64; i++)
265                 block[i] = block1[i];
266         }
267
268         dct->func(block);
269         mmx_emms();
270
271         if (dct->format == SCALE_PERM) {
272             for (i = 0; i < 64; i++) {
273                 scale = 8 * (1 << (AANSCALE_BITS + 11)) / ff_aanscales[i];
274                 block[i] = (block[i] * scale) >> AANSCALE_BITS;
275             }
276         }
277
278         ref(block1);
279
280         blockSumErr = 0;
281         for (i = 0; i < 64; i++) {
282             int err = block[i] - block1[i];
283             err_sum += err;
284             v = abs(err);
285             if (v > err_inf)
286                 err_inf = v;
287             err2 += v * v;
288             sysErr[i] += block[i] - block1[i];
289             blockSumErr += v;
290             if (abs(block[i]) > maxout)
291                 maxout = abs(block[i]);
292         }
293         if (blockSumErrMax < blockSumErr)
294             blockSumErrMax = blockSumErr;
295     }
296     for (i = 0; i < 64; i++)
297         sysErrMax = FFMAX(sysErrMax, FFABS(sysErr[i]));
298
299     for (i = 0; i < 64; i++) {
300         if (i % 8 == 0)
301             printf("\n");
302         printf("%7d ", (int) sysErr[i]);
303     }
304     printf("\n");
305
306     omse = (double) err2 / NB_ITS / 64;
307     ome  = (double) err_sum / NB_ITS / 64;
308
309     spec_err = is_idct && (err_inf > 1 || omse > 0.02 || fabs(ome) > 0.0015);
310
311     printf("%s %s: ppe=%d omse=%0.8f ome=%0.8f syserr=%0.8f maxout=%d blockSumErr=%d\n",
312            is_idct ? "IDCT" : "DCT", dct->name, err_inf,
313            omse, ome, (double) sysErrMax / NB_ITS,
314            maxout, blockSumErrMax);
315
316     if (spec_err && !dct->nonspec)
317         return 1;
318
319     if (!speed)
320         return 0;
321
322     /* speed test */
323     for (i = 0; i < 64; i++)
324         block1[i] = 0;
325
326     switch (test) {
327     case 0:
328         for (i = 0; i < 64; i++)
329             block1[i] = av_lfg_get(&prng) % 512 - 256;
330         if (is_idct) {
331             ff_ref_fdct(block1);
332             for (i = 0; i < 64; i++)
333                 block1[i] >>= 3;
334         }
335         break;
336     case 1:
337     case 2:
338         block1[0] = av_lfg_get(&prng) % 512 - 256;
339         block1[1] = av_lfg_get(&prng) % 512 - 256;
340         block1[2] = av_lfg_get(&prng) % 512 - 256;
341         block1[3] = av_lfg_get(&prng) % 512 - 256;
342         break;
343     }
344
345     if (dct->format == MMX_PERM) {
346         for (i = 0; i < 64; i++)
347             block[idct_mmx_perm[i]] = block1[i];
348     } else if (dct->format == MMX_SIMPLE_PERM) {
349         for (i = 0; i < 64; i++)
350             block[idct_simple_mmx_perm[i]] = block1[i];
351     } else {
352         for (i = 0; i < 64; i++)
353             block[i] = block1[i];
354     }
355
356     ti = gettime();
357     it1 = 0;
358     do {
359         for (it = 0; it < NB_ITS_SPEED; it++) {
360             for (i = 0; i < 64; i++)
361                 block[i] = block1[i];
362             dct->func(block);
363         }
364         it1 += NB_ITS_SPEED;
365         ti1 = gettime() - ti;
366     } while (ti1 < 1000000);
367     mmx_emms();
368
369     printf("%s %s: %0.1f kdct/s\n", is_idct ? "IDCT" : "DCT", dct->name,
370            (double) it1 * 1000.0 / (double) ti1);
371
372     return 0;
373 }
374
375 DECLARE_ALIGNED(8, static uint8_t, img_dest)[64];
376 DECLARE_ALIGNED(8, static uint8_t, img_dest1)[64];
377
378 static void idct248_ref(uint8_t *dest, int linesize, int16_t *block)
379 {
380     static int init;
381     static double c8[8][8];
382     static double c4[4][4];
383     double block1[64], block2[64], block3[64];
384     double s, sum, v;
385     int i, j, k;
386
387     if (!init) {
388         init = 1;
389
390         for (i = 0; i < 8; i++) {
391             sum = 0;
392             for (j = 0; j < 8; j++) {
393                 s = (i == 0) ? sqrt(1.0 / 8.0) : sqrt(1.0 / 4.0);
394                 c8[i][j] = s * cos(M_PI * i * (j + 0.5) / 8.0);
395                 sum += c8[i][j] * c8[i][j];
396             }
397         }
398
399         for (i = 0; i < 4; i++) {
400             sum = 0;
401             for (j = 0; j < 4; j++) {
402                 s = (i == 0) ? sqrt(1.0 / 4.0) : sqrt(1.0 / 2.0);
403                 c4[i][j] = s * cos(M_PI * i * (j + 0.5) / 4.0);
404                 sum += c4[i][j] * c4[i][j];
405             }
406         }
407     }
408
409     /* butterfly */
410     s = 0.5 * sqrt(2.0);
411     for (i = 0; i < 4; i++) {
412         for (j = 0; j < 8; j++) {
413             block1[8 * (2 * i) + j] =
414                 (block[8 * (2 * i) + j] + block[8 * (2 * i + 1) + j]) * s;
415             block1[8 * (2 * i + 1) + j] =
416                 (block[8 * (2 * i) + j] - block[8 * (2 * i + 1) + j]) * s;
417         }
418     }
419
420     /* idct8 on lines */
421     for (i = 0; i < 8; i++) {
422         for (j = 0; j < 8; j++) {
423             sum = 0;
424             for (k = 0; k < 8; k++)
425                 sum += c8[k][j] * block1[8 * i + k];
426             block2[8 * i + j] = sum;
427         }
428     }
429
430     /* idct4 */
431     for (i = 0; i < 8; i++) {
432         for (j = 0; j < 4; j++) {
433             /* top */
434             sum = 0;
435             for (k = 0; k < 4; k++)
436                 sum += c4[k][j] * block2[8 * (2 * k) + i];
437             block3[8 * (2 * j) + i] = sum;
438
439             /* bottom */
440             sum = 0;
441             for (k = 0; k < 4; k++)
442                 sum += c4[k][j] * block2[8 * (2 * k + 1) + i];
443             block3[8 * (2 * j + 1) + i] = sum;
444         }
445     }
446
447     /* clamp and store the result */
448     for (i = 0; i < 8; i++) {
449         for (j = 0; j < 8; j++) {
450             v = block3[8 * i + j];
451             if      (v < 0)   v = 0;
452             else if (v > 255) v = 255;
453             dest[i * linesize + j] = (int) rint(v);
454         }
455     }
456 }
457
458 static void idct248_error(const char *name,
459                           void (*idct248_put)(uint8_t *dest, int line_size,
460                                               int16_t *block),
461                           int speed)
462 {
463     int it, i, it1, ti, ti1, err_max, v;
464     AVLFG prng;
465
466     av_lfg_init(&prng, 1);
467
468     /* just one test to see if code is correct (precision is less
469        important here) */
470     err_max = 0;
471     for (it = 0; it < NB_ITS; it++) {
472         /* XXX: use forward transform to generate values */
473         for (i = 0; i < 64; i++)
474             block1[i] = av_lfg_get(&prng) % 256 - 128;
475         block1[0] += 1024;
476
477         for (i = 0; i < 64; i++)
478             block[i] = block1[i];
479         idct248_ref(img_dest1, 8, block);
480
481         for (i = 0; i < 64; i++)
482             block[i] = block1[i];
483         idct248_put(img_dest, 8, block);
484
485         for (i = 0; i < 64; i++) {
486             v = abs((int) img_dest[i] - (int) img_dest1[i]);
487             if (v == 255)
488                 printf("%d %d\n", img_dest[i], img_dest1[i]);
489             if (v > err_max)
490                 err_max = v;
491         }
492     }
493     printf("%s %s: err_inf=%d\n", 1 ? "IDCT248" : "DCT248", name, err_max);
494
495     if (!speed)
496         return;
497
498     ti = gettime();
499     it1 = 0;
500     do {
501         for (it = 0; it < NB_ITS_SPEED; it++) {
502             for (i = 0; i < 64; i++)
503                 block[i] = block1[i];
504             idct248_put(img_dest, 8, block);
505         }
506         it1 += NB_ITS_SPEED;
507         ti1 = gettime() - ti;
508     } while (ti1 < 1000000);
509     mmx_emms();
510
511     printf("%s %s: %0.1f kdct/s\n", 1 ? "IDCT248" : "DCT248", name,
512            (double) it1 * 1000.0 / (double) ti1);
513 }
514
515 static void help(void)
516 {
517     printf("dct-test [-i] [<test-number>]\n"
518            "test-number 0 -> test with random matrixes\n"
519            "            1 -> test with random sparse matrixes\n"
520            "            2 -> do 3. test from mpeg4 std\n"
521            "-i          test IDCT implementations\n"
522            "-4          test IDCT248 implementations\n"
523            "-t          speed test\n");
524 }
525
526 int main(int argc, char **argv)
527 {
528     int test_idct = 0, test_248_dct = 0;
529     int c, i;
530     int test = 1;
531     int speed = 0;
532     int err = 0;
533
534     cpu_flags = av_get_cpu_flags();
535
536     ff_ref_dct_init();
537     idct_mmx_init();
538
539     for (i = 0; i < 256; i++)
540         cropTbl[i + MAX_NEG_CROP] = i;
541     for (i = 0; i < MAX_NEG_CROP; i++) {
542         cropTbl[i] = 0;
543         cropTbl[i + MAX_NEG_CROP + 256] = 255;
544     }
545
546     for (;;) {
547         c = getopt(argc, argv, "ih4t");
548         if (c == -1)
549             break;
550         switch (c) {
551         case 'i':
552             test_idct = 1;
553             break;
554         case '4':
555             test_248_dct = 1;
556             break;
557         case 't':
558             speed = 1;
559             break;
560         default:
561         case 'h':
562             help();
563             return 0;
564         }
565     }
566
567     if (optind < argc)
568         test = atoi(argv[optind]);
569
570     printf("ffmpeg DCT/IDCT test\n");
571
572     if (test_248_dct) {
573         idct248_error("SIMPLE-C", ff_simple_idct248_put, speed);
574     } else {
575         const struct algo *algos = test_idct ? idct_tab : fdct_tab;
576         for (i = 0; algos[i].name; i++)
577             if (!(~cpu_flags & algos[i].mm_support)) {
578                 err |= dct_error(&algos[i], test, test_idct, speed);
579             }
580     }
581
582     return err;
583 }