1 /*
2 * Assembly testing and benchmarking tool
3 * Copyright (c) 2015 Henrik Gramner
4 * Copyright (c) 2008 Loren Merritt
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Copyright © 2018, VideoLAN and dav1d authors
23 * Copyright © 2018, Two Orioles, LLC
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions are met:
28 *
29 * 1. Redistributions of source code must retain the above copyright notice, this
30 * list of conditions and the following disclaimer.
31 *
32 * 2. Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
40 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 */
47
48 #include "config.h"
49 #include "config_components.h"
50
51 #ifndef _GNU_SOURCE
52 # define _GNU_SOURCE // for syscall (performance monitoring API), strsignal()
53 #endif
54
55 #include <signal.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
66
67 #if HAVE_IO_H
68 #include <io.h>
69 #endif
70 #if HAVE_PRCTL
71 #include <sys/prctl.h>
72 #endif
73
74 #if defined(_WIN32) && !defined(SIGBUS)
75 /* non-standard, use the same value as mingw-w64 */
76 #define SIGBUS 10
77 #endif
78
79 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
80 #include <windows.h>
81 #define COLOR_RED FOREGROUND_RED
82 #define COLOR_GREEN FOREGROUND_GREEN
83 #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
84 #else
87 #define COLOR_YELLOW 3
88 #endif
89
90 #if HAVE_UNISTD_H
91 #include <unistd.h>
92 #endif
93
94 #if !HAVE_ISATTY
96 #endif
97
98 #if ARCH_AARCH64
100 #elif ARCH_RISCV
102 #endif
103
104 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
106
107 void (*checkasm_checked_call)(
void *
func,
int dummy, ...) = checkasm_checked_call_novfp;
108 #endif
109
110 /* Trade-off between speed and accuracy */
112
113 /* List of tests to invoke */
114 static const struct {
118 #if CONFIG_AVCODEC
119 #if CONFIG_AAC_DECODER
122 #endif
123 #if CONFIG_AAC_ENCODER
125 #endif
126 #if CONFIG_AC3DSP
128 #endif
129 #if CONFIG_ALAC_DECODER
131 #endif
132 #if CONFIG_APV_DECODER
134 #endif
135 #if CONFIG_AUDIODSP
137 #endif
138 #if CONFIG_BLOCKDSP
140 #endif
141 #if CONFIG_BSWAPDSP
143 #endif
144 #if CONFIG_CAVS_DECODER
146 #endif
147 #if CONFIG_DCA_DECODER
150 #endif
151 #if CONFIG_DIRAC_DECODER
153 #endif
154 #if CONFIG_EXR_DECODER
156 #endif
157 #if CONFIG_FDCTDSP
159 #endif
160 #if CONFIG_FLAC_DECODER
162 #endif
163 #if CONFIG_FMTCONVERT
165 #endif
166 #if CONFIG_G722DSP
168 #endif
169 #if CONFIG_H263DSP
171 #endif
172 #if CONFIG_H264CHROMA
174 #endif
175 #if CONFIG_H264DSP
177 #endif
178 #if CONFIG_H264PRED
180 #endif
181 #if CONFIG_H264QPEL
183 #endif
184 #if CONFIG_HEVC_DECODER
190 #endif
191 #if CONFIG_HPELDSP
193 #endif
194 #if CONFIG_HUFFYUV_DECODER
196 #endif
197 #if CONFIG_IDCTDSP
199 #endif
200 #if CONFIG_JPEG2000_DECODER
202 #endif
203 #if CONFIG_LLAUDDSP
205 #endif
206 #if CONFIG_HUFFYUVDSP
208 #endif
209 #if CONFIG_LLVIDENCDSP
211 #endif
212 #if CONFIG_LPC
214 #endif
215 #if CONFIG_ME_CMP
217 #endif
218 #if CONFIG_MPEGVIDEOENCDSP
220 #endif
221 #if CONFIG_OPUS_DECODER
223 #endif
224 #if CONFIG_PIXBLOCKDSP
226 #endif
227 #if CONFIG_QPELDSP
229 #endif
230 #if CONFIG_RV34DSP
232 #endif
233 #if CONFIG_RV40_DECODER
235 #endif
236 #if CONFIG_SVQ1_ENCODER
238 #endif
239 #if CONFIG_TAK_DECODER
241 #endif
242 #if CONFIG_UTVIDEO_DECODER
244 #endif
245 #if CONFIG_V210_DECODER
247 #endif
248 #if CONFIG_V210_ENCODER
250 #endif
251 #if CONFIG_VC1DSP
253 #endif
254 #if CONFIG_VP3DSP
256 #endif
257 #if CONFIG_VP8DSP
259 #endif
260 #if CONFIG_VP9_DECODER
262 #endif
263 #if CONFIG_VIDEODSP
265 #endif
266 #if CONFIG_VORBIS_DECODER
268 #endif
269 #if CONFIG_VVC_DECODER
273 #endif
274 #endif
275 #if CONFIG_AVFILTER
276 #if CONFIG_SCENE_SAD
278 #endif
279 #if CONFIG_AFIR_FILTER
281 #endif
282 #if CONFIG_BLACKDETECT_FILTER
284 #endif
285 #if CONFIG_BLEND_FILTER
287 #endif
288 #if CONFIG_BWDIF_FILTER
290 #endif
291 #if CONFIG_COLORDETECT_FILTER
293 #endif
294 #if CONFIG_COLORSPACE_FILTER
296 #endif
297 #if CONFIG_EQ_FILTER
299 #endif
300 #if CONFIG_FSPP_FILTER
302 #endif
303 #if CONFIG_GBLUR_FILTER
305 #endif
306 #if CONFIG_HFLIP_FILTER
308 #endif
309 #if CONFIG_IDET_FILTER
311 #endif
312 #if CONFIG_NLMEANS_FILTER
314 #endif
315 #if CONFIG_THRESHOLD_FILTER
317 #endif
318 #if CONFIG_SOBEL_FILTER
320 #endif
321 #endif
322 #if CONFIG_SWSCALE
330 #endif
331 #if CONFIG_AVUTIL
337 #endif
339 };
340
341 /* List of cpu flags to check */
342 static const struct {
347 #if ARCH_AARCH64
354 #elif ARCH_ARM
362 #elif ARCH_PPC
366 #elif ARCH_RISCV
376 #elif ARCH_MIPS
379 #elif ARCH_X86
398 #elif ARCH_LOONGARCH
401 #elif ARCH_WASM
403 #endif
405 };
406
414
415 /* Binary search tree node */
419 uint8_t
color;
/* 0 = red, 1 = black */
422
423 /* Internal state */
424 static struct {
433
434 /* perf */
437
446
447 /* PRNG state */
449
450 /* float compare support code */
452 {
454 }
455
457 {
459
462
464 // handle -0.0 == +0.0
466 }
467
468 if (llabs((
int64_t)x.
i - y.
i) <= max_ulp)
469 return 1;
470
471 return 0;
472 }
473
476 {
478
479 for (
i = 0;
i <
len;
i++) {
481 return 0;
482 }
483 return 1;
484 }
485
487 {
489 if (abs_diff < eps)
490 return 1;
491
492 fprintf(stderr,
"test failed comparing %g with %g (abs diff=%g with EPS=%g)\n",
a,
b, abs_diff, eps);
493
494 return 0;
495 }
496
499 {
501
502 for (
i = 0;
i <
len;
i++) {
504 return 0;
505 }
506 return 1;
507 }
508
510 {
512 }
513
515 unsigned max_ulp,
unsigned len)
516 {
518
519 for (
i = 0;
i <
len;
i++) {
521 return 0;
522 }
523 return 1;
524 }
525
527 {
528 double abs_diff =
fabs(
a -
b);
529
530 return abs_diff < eps;
531 }
532
535 {
537
538 for (
i = 0;
i <
len;
i++) {
540 return 0;
541 }
542 return 1;
543 }
544
545 /* Print colored text to stderr if the terminal supports it */
547 {
550
551 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
552 static HANDLE con;
553 static WORD org_attributes;
554
556 CONSOLE_SCREEN_BUFFER_INFO con_info;
557 con = GetStdHandle(STD_ERROR_HANDLE);
558 if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
559 org_attributes = con_info.wAttributes;
561 } else
563 }
565 SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (
color & 0x0f));
566 #else
568 const char *term = getenv("TERM");
570 }
572 fprintf(stderr,
"\x1b[%d;3%dm", (
color & 0x08) >> 3,
color & 0x07);
573 #endif
574
576 vfprintf(stderr, fmt,
arg);
578
580 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
581 SetConsoleTextAttribute(con, org_attributes);
582 #else
583 fprintf(stderr, "\x1b[0m");
584 #endif
585 }
586 }
587
588 /* Deallocate a tree */
590 {
593 while (v) {
595 free(v);
596 v = next;
597 }
598
602 }
603 }
604
605 /* Allocate a zero-initialized block, clean up and exit on failure */
607 {
608 void *ptr = calloc(1,
size);
609 if (!ptr) {
610 fprintf(stderr, "checkasm: malloc failed\n");
612 exit(1);
613 }
614 return ptr;
615 }
616
617 /* Get the suffix of the specified cpu flag */
619 {
621
624 return cpus[
i].suffix;
625
626 return "c";
627 }
628
630 {
631 return *(
const uint16_t*)
a - *(
const uint16_t*)
b;
632 }
633
634 /* Measure the overhead of the timing code (in decicycles) */
636 {
637 uint16_t nops[10000];
640
641 uint64_t t = 0;
642 for (
i = 0;
i < 10000;
i++) {
646 }
647
648 qsort(nops, 10000,
sizeof(uint16_t),
cmp_nop);
649 for (
i = 2500;
i < 7500;
i++)
651
652 return nop_sum / 500;
653 }
654
656 {
658 const double cycles = (
double)(10 *
p->cycles) /
p->iterations -
state.nop_time;
659 if (cycles > 0.0)
660 return cycles / 32.0; /* 32 calls per iteration */
661 }
662 return 0.0;
663 }
664
665 /* Print benchmark results */
667 {
672 double decicycles;
673
675
676 do {
681 const char sep =
state.csv ?
',' :
'\t';
682 printf(
"%s%c%s%c%.1f\n",
f->name, sep,
684 decicycles / 10.0);
685 } else {
686 const int pad_length = 10 + 50 -
688 const double ratio = decicycles ?
689 baseline / decicycles : 0.0;
691 decicycles / 10.0, ratio);
692 }
693 }
694 }
while ((v = v->
next));
695
697 }
698 }
699
700 /* ASCIIbetical sort except preserving natural order for numbers */
702 {
703 const char *start =
a;
704 int ascii_diff, digit_diff;
705
706 for (; !(ascii_diff = *(
const unsigned char*)
a - *(
const unsigned char*)
b) && *
a;
a++,
b++);
708
710 return digit_diff;
711
712 return ascii_diff;
713 }
714
715 /* Perform a tree rotation in the specified direction and return the new root */
717 {
719 f->child[dir^1] =
r->child[dir];
724 }
725
726 #define is_red(f) ((f) && !(f)->color)
727
728 /* Balance a left-leaning red-black tree at the specified node */
730 {
732
735 f->child[0]->color =
f->child[1]->color = 1;
736 }
737
742 }
743
744 /* Get a node with the specified name, creating it if it doesn't exist */
746 {
748
750 /* Search the tree for a matching node */
754
755 /* Rebalance the tree on the way up if a new node was inserted */
756 if (!
f->versions.func)
758 }
759 } else {
760 /* Allocate and insert a new node into the tree */
761 int name_length = strlen(
name);
763 memcpy(
f->name,
name, name_length + 1);
764 }
765
767 }
768
770
771 /* Crash handling: attempt to catch crashes and handle them
772 * gracefully instead of just aborting abruptly. */
773 #ifdef _WIN32
774 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
777
778 if (!
state.catch_signals)
779 return EXCEPTION_CONTINUE_SEARCH;
780
781 switch (e->ExceptionRecord->ExceptionCode) {
782 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
783 case EXCEPTION_INT_DIVIDE_BY_ZERO:
785 break;
786 case EXCEPTION_ILLEGAL_INSTRUCTION:
787 case EXCEPTION_PRIV_INSTRUCTION:
789 break;
790 case EXCEPTION_ACCESS_VIOLATION:
791 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
792 case EXCEPTION_DATATYPE_MISALIGNMENT:
793 case EXCEPTION_STACK_OVERFLOW:
795 break;
796 case EXCEPTION_IN_PAGE_ERROR:
798 break;
799 default:
800 return EXCEPTION_CONTINUE_SEARCH;
801 }
802 state.catch_signals = 0;
804 return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
805 }
806 #endif
807 #elif !defined(_WASI_EMULATED_SIGNAL)
809
812 .sa_flags = SA_RESETHAND,
813 };
814
816 if (
state.catch_signals) {
817 state.catch_signals = 0;
820 }
821 }
822 #endif
823
824 /* Compares a string with a wildcard pattern. */
826 {
827 const char *wild = strchr(pattern, '*');
828 if (wild) {
829 const size_t len = wild - pattern;
830 if (strncmp(str, pattern,
len))
return 1;
831 while (*++wild == '*');
832 if (!*wild) return 0;
835 return !*str;
836 }
837 return strcmp(str, pattern);
838 }
839
840 /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
842 {
843 int old_cpu_flag =
state.cpu_flag;
844
845 flag |= old_cpu_flag;
849
850 if (!
flag ||
state.cpu_flag != old_cpu_flag) {
852
856 continue;
859 }
860 }
861 }
862
863 /* Print the name of the current CPU flag, but only do it once */
865 {
866 if (
state.cpu_flag_name) {
869 }
870 }
871
872 #if CONFIG_LINUX_PERF
873 static int bench_init_linux(void)
874 {
875 struct perf_event_attr attr = {
876 .type = PERF_TYPE_HARDWARE,
877 .size = sizeof(struct perf_event_attr),
878 .
config = PERF_COUNT_HW_CPU_CYCLES,
879 .disabled = 1, // start counting only on demand
880 .exclude_kernel = 1,
881 .exclude_hv = 1,
882 #if !ARCH_X86
883 .exclude_guest = 1,
884 #endif
885 };
886
887 fprintf(stderr, "benchmarking with Linux Perf Monitoring API\n");
888
889 state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
890 if (
state.sysfd == -1) {
891 perror("perf_event_open");
892 return -1;
893 }
894 return 0;
895 }
896 #elif CONFIG_MACOS_KPERF
897 static int bench_init_kperf(void)
898 {
900 return 0;
901 }
902 #else
904 {
905 #ifdef AV_READ_TIME
910 } else {
911 fprintf(stderr, "checkasm: unable to execute platform specific timer\n");
912 return -1;
913 }
914 fprintf(stderr, "benchmarking with native FFmpeg timers\n");
915 return 0;
916 #else
917 fprintf(stderr, "checkasm: --bench is not supported on your system\n");
918 return -1;
919 #endif
920 }
921 #endif
922
924 {
925 #if CONFIG_LINUX_PERF
926 int ret = bench_init_linux();
927 #elif CONFIG_MACOS_KPERF
928 int ret = bench_init_kperf();
929 #else
931 #endif
934
936 fprintf(stderr,
"nop: %d.%d\n",
state.nop_time/10,
state.nop_time%10);
937 return 0;
938 }
939
941 {
942 #if CONFIG_LINUX_PERF
944 #endif
945 }
946
948 {
949 fprintf(stderr,
950 "Usage: %s [options...] [seed]\n"
951 " --test=<pattern> Run specific test.\n"
952 " --bench Run benchmark.\n"
953 " --csv, --tsv Output results in rows of comma or tab separated values.\n"
954 " --runs=<ptwo> Manual number of benchmark iterations to run 2**<ptwo>.\n"
955 " --verbose Increase verbosity.\n",
956 path);
957 return 1;
958 }
959
960 int main(
int argc,
char *argv[])
961 {
964 char arch_info_buf[50] = "";
965
966 #ifdef _WIN32
967 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
969 #endif
970 #elif !defined(_WASI_EMULATED_SIGNAL)
975 #endif
976 #if HAVE_PRCTL && defined(PR_SET_UNALIGN)
977 prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS);
978 #endif
979 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
981 checkasm_checked_call = checkasm_checked_call_vfp;
982 #endif
983
985 fprintf(stderr, "checkasm: no tests to perform\n");
986 return 0;
987 }
988
989 for (
i = 1;
i < argc;
i++) {
990 const char *
arg = argv[
i];
991 unsigned long l;
992 char *end;
993
994 if (!strncmp(
arg,
"--bench", 7)) {
996 return 1;
999 state.bench_pattern_len = strlen(
state.bench_pattern);
1000 } else
1001 state.bench_pattern =
"*";
1002 }
else if (!strncmp(
arg,
"--test=", 7)) {
1004 }
else if (!strcmp(
arg,
"--csv")) {
1006 }
else if (!strcmp(
arg,
"--tsv")) {
1008 }
else if (!strcmp(
arg,
"--verbose") || !strcmp(
arg,
"-v")) {
1010 }
else if (!strncmp(
arg,
"--runs=", 7)) {
1011 l = strtoul(
arg + 7, &end, 10);
1012 if (*end == '0円') {
1013 if (l > 30) {
1014 fprintf(stderr, "checkasm: error: runs exponent must be within the range 0 <= 30\n");
1016 }
1018 } else {
1019 return usage(argv[0]);
1020 }
1021 }
else if ((l = strtoul(
arg, &end, 10)) <= UINT_MAX &&
1022 *end == '0円') {
1024 } else {
1025 return usage(argv[0]);
1026 }
1027 }
1028
1029 #if ARCH_AARCH64 && HAVE_SVE
1031 snprintf(arch_info_buf,
sizeof(arch_info_buf),
1032 "SVE %d bits, ", 8 * ff_aarch64_sve_length());
1033 #elif ARCH_RISCV && HAVE_RVV
1035 snprintf(arch_info_buf,
sizeof (arch_info_buf),
1036 "%zu-bit vectors, ", 8 * ff_get_rv_vlenb());
1037 #endif
1038 fprintf(stderr,
"checkasm: %susing random seed %u\n", arch_info_buf,
seed);
1040
1041 if (
state.bench_pattern)
1043
1045 for (
i = 0;
cpus[
i].flag;
i++)
1047
1048 if (
state.num_failed) {
1049 fprintf(stderr,
"checkasm: %d of %d tests have failed\n",
state.num_failed,
state.num_checked);
1051 } else {
1052 fprintf(stderr,
"checkasm: all %d tests passed\n",
state.num_checked);
1053 if (
state.bench_pattern) {
1055 }
1056 }
1057
1061 }
1062
1063 /* Decide whether or not the specified function needs to be tested and
1064 * allocate/initialize data structures if needed. Returns a pointer to a
1065 * reference function if the function should be tested, otherwise NULL */
1067 {
1068 char name_buf[256];
1071 int name_length;
1073
1077
1078 if (!
func || name_length <= 0 || name_length >=
sizeof(name_buf))
1080
1082 state.funcs->color = 1;
1083 v = &
state.current_func->versions;
1084
1087 do {
1088 /* Only test functions that haven't already been tested */
1091
1094
1095 prev = v;
1096 }
while ((v = v->
next));
1097
1099 }
1100
1104 state.current_func_ver = v;
1105
1107 state.num_checked++;
1108
1110 }
1111
1112 /* Decide whether or not the current function needs to be benchmarked */
1114 {
1115 return !
state.num_failed &&
state.bench_pattern &&
1117 }
1118
1119 /* Indicate that the current test has failed, return whether verbose printing
1120 * is requested. */
1122 {
1123 if (
state.current_func_ver &&
state.current_func_ver->cpu &&
1124 state.current_func_ver->ok)
1125 {
1127
1129 fprintf(stderr,
" %s_%s (",
state.current_func->name,
cpu_suffix(
state.current_func_ver->cpu));
1131 vfprintf(stderr, msg,
arg);
1133 fprintf(stderr, ")\n");
1134
1135 state.current_func_ver->ok = 0;
1137 }
1138 return state.verbose;
1139 }
1140
1142 state.catch_signals = enabled;
1143 }
1144
1147 #ifdef __GLIBC__
1149 #else
1151 s == SIGILL ?
"illegal instruction" :
1152 s == SIGBUS ?
"bus error" :
1153 "segmentation fault");
1154 #endif
1155 }
1157 }
1158
1159 /* Get the benchmark context of the current function */
1161 {
1163 memset(perf, 0, sizeof(*perf));
1165 return perf;
1166 }
1167
1168 /* Print the outcome of all tests performed since the last time this function was called */
1170 {
1171 static int prev_checked, prev_failed, max_length;
1172
1173 if (
state.num_checked > prev_checked) {
1174 int pad_length = max_length + 4;
1176
1178 pad_length -= fprintf(stderr,
" - %s.",
state.current_test_name);
1180 pad_length -= vfprintf(stderr,
name,
arg);
1182 fprintf(stderr,
"%*c",
FFMAX(pad_length, 0) + 2,
'[');
1183
1184 if (
state.num_failed == prev_failed)
1186 else
1188 fprintf(stderr, "]\n");
1189
1190 prev_checked =
state.num_checked;
1191 prev_failed =
state.num_failed;
1192 }
else if (!
state.cpu_flag) {
1193 /* Calculate the amount of padding required to make the output vertically aligned */
1194 int length = strlen(
state.current_test_name);
1196
1200
1201 if (length > max_length)
1202 max_length = length;
1203 }
1204 }
1205
1207 const char *
name,
int w,
int h,
1208 int *err)
1209 {
1210 if (*err)
1211 return 0;
1213 return 1;
1214 *err = 1;
1215 fprintf(stderr,
"%s (%dx%d):\n",
name,
w,
h);
1216 return 0;
1217 }
1218
1219 #define DEF_CHECKASM_CHECK_BODY(compare, type, fmt) \
1220 do { \
1221 int64_t aligned_w = (w - 1LL + align_w) & ~(align_w - 1); \
1222 int64_t aligned_h = (h - 1LL + align_h) & ~(align_h - 1); \
1223 int err = 0; \
1224 int y = 0; \
1225 av_assert0(aligned_w == (int32_t)aligned_w);\
1226 av_assert0(aligned_h == (int32_t)aligned_h);\
1227 stride1 /= sizeof(*buf1); \
1228 stride2 /= sizeof(*buf2); \
1229 for (y = 0; y < h; y++) \
1230 if (!compare(&buf1[y*stride1], &buf2[y*stride2], w)) \
1231 break; \
1232 if (y != h) { \
1233 if (check_err(file, line, name, w, h, &err)) \
1234 return 1; \
1235 for (y = 0; y < h; y++) { \
1236 for (int x = 0; x < w; x++) \
1237 fprintf(stderr, " " fmt, buf1[x]); \
1238 fprintf(stderr, " "); \
1239 for (int x = 0; x < w; x++) \
1240 fprintf(stderr, " " fmt, buf2[x]); \
1241 fprintf(stderr, " "); \
1242 for (int x = 0; x < w; x++) \
1243 fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
1244 buf1 += stride1; \
1245 buf2 += stride2; \
1246 fprintf(stderr, "\n"); \
1247 } \
1248 buf1 -= h*stride1; \
1249 buf2 -= h*stride2; \
1250 } \
1251 for (y = -padding; y < 0; y++) \
1252 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1253 w + 2*padding)) { \
1254 if (check_err(file, line, name, w, h, &err)) \
1255 return 1; \
1256 fprintf(stderr, " overwrite above\n"); \
1257 break; \
1258 } \
1259 for (y = aligned_h; y < aligned_h + padding; y++) \
1260 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1261 w + 2*padding)) { \
1262 if (check_err(file, line, name, w, h, &err)) \
1263 return 1; \
1264 fprintf(stderr, " overwrite below\n"); \
1265 break; \
1266 } \
1267 for (y = 0; y < h; y++) \
1268 if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
1269 padding)) { \
1270 if (check_err(file, line, name, w, h, &err)) \
1271 return 1; \
1272 fprintf(stderr, " overwrite left\n"); \
1273 break; \
1274 } \
1275 for (y = 0; y < h; y++) \
1276 if (!compare(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \
1277 padding)) { \
1278 if (check_err(file, line, name, w, h, &err)) \
1279 return 1; \
1280 fprintf(stderr, " overwrite right\n"); \
1281 break; \
1282 } \
1283 return err; \
1284 } while (0)
1285
1286 #define cmp_int(a, b, len) (!memcmp(a, b, (len) * sizeof(*(a))))
1287 #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
1288 int checkasm_check_##type(const char *file, int line, \
1289 const type *buf1, ptrdiff_t stride1, \
1290 const type *buf2, ptrdiff_t stride2, \
1291 int w, int h, const char *name, \
1292 int align_w, int align_h, \
1293 int padding) \
1294 { \
1295 DEF_CHECKASM_CHECK_BODY(cmp_int, type, fmt); \
1296 }
1297
1303
1305 const float *buf1, ptrdiff_t stride1,
1306 const float *buf2, ptrdiff_t stride2,
1307 int w,
int h, const
char *
name,
1308 unsigned max_ulp, int align_w, int align_h,
1309 int padding)
1310 {
1311 #define cmp_float(a, b, len) float_near_ulp_array(a, b, max_ulp, len)
1313 #undef cmp_float
1314 }