You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
15 KiB
454 lines
15 KiB
/*
|
|
* test-imgconvert.c - test/time image conversion routines
|
|
* Written by Andrew Church <achurch@achurch.org>
|
|
*
|
|
* This file is part of transcode, a video stream processing tool.
|
|
* transcode is free software, distributable under the terms of the GNU
|
|
* General Public License (version 2 or later). See the file COPYING
|
|
* for details.
|
|
*/
|
|
|
|
#define WIDTH 768 /* Maximum/default width */
|
|
#define HEIGHT 512 /* Maximum/default height */
|
|
#define ITERATIONS 50 /* Minimum # of iterations */
|
|
#define MINTIME 100 /* Minimum msec to iterate */
|
|
|
|
/*************************************************************************/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
#include "config.h"
|
|
#include "aclib/ac.h"
|
|
#include "aclib/imgconvert.h"
|
|
|
|
static void *old_SIGSEGV, *old_SIGILL;
|
|
static int sigsave;
|
|
static sigjmp_buf env;
|
|
|
|
/* Order of formats to test, with name strings */
|
|
static struct {
|
|
ImageFormat fmt;
|
|
const char *name;
|
|
int width_unit, height_unit; /* minimum meaningful unit in X and Y */
|
|
int disabled;
|
|
} fmtlist[] = {
|
|
{ IMG_YUV420P, "420P", 2, 2 },
|
|
{ IMG_YV12, "YV12", 2, 2, 1 }, /* disabled by default */
|
|
{ IMG_YUV411P, "411P", 4, 1 },
|
|
{ IMG_YUV422P, "422P", 2, 1 },
|
|
{ IMG_YUV444P, "444P", 1, 1 },
|
|
{ IMG_YUY2, "YUY2", 2, 1 },
|
|
{ IMG_UYVY, "UYVY", 2, 1 },
|
|
{ IMG_YVYU, "YVYU", 2, 1 },
|
|
{ IMG_Y8, " Y8 ", 1, 1 },
|
|
{ IMG_RGB24, "RGB ", 1, 1 },
|
|
{ IMG_BGR24, "BGR ", 1, 1 },
|
|
{ IMG_RGBA32, "RGBA", 1, 1 },
|
|
{ IMG_ABGR32, "ABGR", 1, 1 },
|
|
{ IMG_ARGB32, "ARGB", 1, 1 },
|
|
{ IMG_BGRA32, "BGRA", 1, 1 },
|
|
{ IMG_GRAY8, "GRAY", 1, 1 },
|
|
{ IMG_NONE, NULL }
|
|
};
|
|
|
|
/*************************************************************************/
|
|
|
|
static void sighandler(int sig)
|
|
{
|
|
sigsave = sig;
|
|
siglongjmp(env, 1);
|
|
}
|
|
|
|
static void set_signals(void)
|
|
{
|
|
old_SIGSEGV = signal(SIGSEGV, sighandler);
|
|
old_SIGILL = signal(SIGILL , sighandler);
|
|
}
|
|
|
|
static void clear_signals(void)
|
|
{
|
|
signal(SIGSEGV, old_SIGSEGV);
|
|
signal(SIGILL , old_SIGILL );
|
|
}
|
|
|
|
/* Return value: >=0 is time/iteration in usec, <0 is error
|
|
* -1: unknown error
|
|
* -2: ac_init(0) failed
|
|
* -3: ac_init(accel) failed
|
|
* -4: ac_imgconvert(0) failed
|
|
* -5: ac_imgconvert(accel) failed
|
|
* -6: compare failed
|
|
* -7: SIGSEGV
|
|
* -8: SIGILL
|
|
* If `check' is nonzero, just checks for accuracy and returns error or 0.
|
|
*/
|
|
static int testit(uint8_t *srcimage, ImageFormat srcfmt, ImageFormat destfmt,
|
|
int width, int height, int accel, int verbose, int check)
|
|
{
|
|
static __attribute__((aligned(16))) uint8_t srcbuf[WIDTH*HEIGHT*4],
|
|
destbuf[WIDTH*HEIGHT*4], cmpbuf[WIDTH*HEIGHT*4];
|
|
uint8_t *src[3], *dest[3];
|
|
long long tdiff;
|
|
unsigned long long start, stop;
|
|
struct rusage ru;
|
|
int i, icnt;
|
|
|
|
memset(cmpbuf, 0, sizeof(cmpbuf));
|
|
memset(destbuf, 0, sizeof(destbuf));
|
|
|
|
sigsave = 0;
|
|
set_signals();
|
|
if (sigsetjmp(env, 1)) {
|
|
clear_signals();
|
|
return sigsave==SIGILL ? -8 : -7;
|
|
}
|
|
|
|
if (!ac_init(0))
|
|
return -2;
|
|
ac_memcpy(srcbuf, srcimage, sizeof(srcbuf));
|
|
src[0] = srcbuf;
|
|
if (IS_YUV_FORMAT(srcfmt))
|
|
YUV_INIT_PLANES(src, srcbuf, srcfmt, width, height);
|
|
dest[0] = cmpbuf;
|
|
if (IS_YUV_FORMAT(destfmt))
|
|
YUV_INIT_PLANES(dest, cmpbuf, destfmt, width, height);
|
|
if (!ac_imgconvert(src, srcfmt, dest, destfmt, width, height))
|
|
return -4;
|
|
|
|
if (!ac_init(accel))
|
|
return -3;
|
|
// currently src can get destroyed--see img_yuv_mixed.c
|
|
ac_memcpy(srcbuf, srcimage, sizeof(srcbuf));
|
|
dest[0] = destbuf;
|
|
if (IS_YUV_FORMAT(destfmt))
|
|
YUV_INIT_PLANES(dest, destbuf, destfmt, width, height);
|
|
if (!ac_imgconvert(src, srcfmt, dest, destfmt, width, height))
|
|
return -5;
|
|
|
|
tdiff = 0;
|
|
for (i = 0; i < sizeof(destbuf); i++) {
|
|
int diff = (int)destbuf[i] - (int)cmpbuf[i];
|
|
if (diff < -1 || diff > 1) {
|
|
if (verbose) {
|
|
fprintf(stderr, "*** compare error: at %d (want=%d have=%d)\n",
|
|
i, cmpbuf[i], destbuf[i]);
|
|
}
|
|
return -6;
|
|
}
|
|
tdiff += diff*diff;
|
|
}
|
|
if (tdiff >= width*height/2) {
|
|
if (verbose) {
|
|
fprintf(stderr,
|
|
"*** compare error: total difference too great (%lld)\n",
|
|
tdiff);
|
|
}
|
|
return -6;
|
|
}
|
|
|
|
if (check)
|
|
return 0;
|
|
|
|
getrusage(RUSAGE_SELF, &ru);
|
|
start = ru.ru_utime.tv_sec * 1000000ULL + ru.ru_utime.tv_usec;
|
|
icnt = 0;
|
|
do {
|
|
for (i = 0; i < ITERATIONS; i++)
|
|
ac_imgconvert(src, srcfmt, dest, destfmt, width, height);
|
|
getrusage(RUSAGE_SELF, &ru);
|
|
stop = ru.ru_utime.tv_sec * 1000000ULL + ru.ru_utime.tv_usec;
|
|
icnt += ITERATIONS;
|
|
} while (stop-start < MINTIME*1000);
|
|
|
|
clear_signals();
|
|
return (stop-start + icnt/2) / icnt;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Check all routines, and return 1 (no failures) or 0 (some failures) */
|
|
|
|
#define TRYIT(w,h) \
|
|
if (testit(srcimage, fmtlist[i].fmt, fmtlist[j].fmt, \
|
|
(w), (h), accel, 1, 1) < -5 \
|
|
) { \
|
|
printf("FAILED: %s -> %s @ %dx%d\n", \
|
|
fmtlist[i].name, fmtlist[j].name, (w), (h)); \
|
|
failures++; \
|
|
}
|
|
|
|
static int checkall(uint8_t *srcimage, int accel, const char *name)
|
|
{
|
|
int i, j;
|
|
int failures = 0;
|
|
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
for (j = 0; fmtlist[j].fmt != IMG_NONE; j++) {
|
|
int oldfail = failures;
|
|
int width_unit = fmtlist[i].width_unit;
|
|
int height_unit = fmtlist[i].height_unit;
|
|
if (fmtlist[j].width_unit > width_unit)
|
|
width_unit = fmtlist[j].width_unit;
|
|
if (fmtlist[j].height_unit > height_unit)
|
|
height_unit = fmtlist[j].height_unit;
|
|
if (name) {
|
|
printf("%s/%s-%s...", name, fmtlist[i].name, fmtlist[j].name);
|
|
fflush(stdout);
|
|
}
|
|
TRYIT(WIDTH, HEIGHT);
|
|
TRYIT(WIDTH-width_unit, HEIGHT);
|
|
TRYIT(WIDTH, HEIGHT-height_unit);
|
|
TRYIT(WIDTH-width_unit, HEIGHT-height_unit);
|
|
if (name && failures == oldfail)
|
|
printf("ok\n");
|
|
}
|
|
}
|
|
if (failures) {
|
|
if (name)
|
|
printf("%s: %d conversions failed.\n", name, failures);
|
|
return 0;
|
|
} else {
|
|
if (name)
|
|
printf("%s: All conversions succeeded.\n", name);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static const char *accel_flags(int accel)
|
|
{
|
|
static char buf[1000];
|
|
snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s",
|
|
!accel ? " none" : "",
|
|
(accel & AC_IA32ASM ) ? " ia32asm" : "",
|
|
(accel & AC_AMD64ASM) ? " amd64asm" : "",
|
|
(accel & AC_CMOVE ) ? " cmove" : "",
|
|
(accel & AC_MMX ) ? " mmx" : "",
|
|
(accel & AC_MMXEXT ) ? " mmxext" : "",
|
|
(accel & AC_3DNOW ) ? " 3dnow" : "",
|
|
(accel & AC_SSE ) ? " sse" : "",
|
|
(accel & AC_SSE2 ) ? " sse2" : "",
|
|
(accel & AC_SSE3 ) ? " sse3" : "");
|
|
return buf;
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
static uint8_t srcbuf[WIDTH*HEIGHT*4];
|
|
int check = 0, accel = 0, compare = 0, verbose = 0, width = WIDTH,
|
|
height = HEIGHT;
|
|
int i, j;
|
|
|
|
while (argc > 1) {
|
|
if (strcmp(argv[--argc],"-h") == 0) {
|
|
fprintf(stderr,
|
|
"Usage: %s [-C] [-c] [-v] [=fmt-name[,fmt-name...]] [@WIDTHxHEIGHT] [accel-name...]\n",
|
|
argv[0]);
|
|
fprintf(stderr,
|
|
"-C: check all testable accelerated routines and exit with success/failure\n"
|
|
"-c: compare with non-accelerated versions and report percentage speedup\n"
|
|
"-v: verbose (report details of comparison failures; with -C, print test names)\n"
|
|
"=: select formats to test\n"
|
|
" fmt-name can be:");
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
char buf[16], *s;
|
|
snprintf(buf, sizeof(buf), "%s", fmtlist[i].name);
|
|
while (*buf && buf[strlen(buf)-1]==' ')
|
|
buf[strlen(buf)-1] = 0;
|
|
s = buf + strspn(buf," ");
|
|
fprintf(stderr," %s", s);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "@: set image size (default/max %dx%d)\n",
|
|
WIDTH, HEIGHT);
|
|
fprintf(stderr, "accel-name can be ia32asm, amd64asm, cmove, mmx, ...\n");
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[argc],"-C") == 0)
|
|
check = 1;
|
|
else if (strcmp(argv[argc],"-c") == 0)
|
|
compare = 1;
|
|
else if (strcmp(argv[argc],"-v") == 0)
|
|
verbose = 1;
|
|
else if (strcmp(argv[argc],"ia32asm") == 0)
|
|
accel |= AC_IA32ASM;
|
|
else if (strcmp(argv[argc],"amd64asm") == 0)
|
|
accel |= AC_AMD64ASM;
|
|
else if (strcmp(argv[argc],"cmove") == 0)
|
|
accel |= AC_CMOVE;
|
|
else if (strcmp(argv[argc],"mmx") == 0)
|
|
accel |= AC_MMX;
|
|
else if (strcmp(argv[argc],"mmxext") == 0)
|
|
accel |= AC_MMXEXT;
|
|
else if (strcmp(argv[argc],"3dnow") == 0)
|
|
accel |= AC_3DNOW;
|
|
else if (strcmp(argv[argc],"3dnowext") == 0)
|
|
accel |= AC_3DNOWEXT;
|
|
else if (strcmp(argv[argc],"sse") == 0)
|
|
accel |= AC_SSE;
|
|
else if (strcmp(argv[argc],"sse2") == 0)
|
|
accel |= AC_SSE2;
|
|
else if (strcmp(argv[argc],"sse3") == 0)
|
|
accel |= AC_SSE3;
|
|
else if (argv[argc][0] == '=') {
|
|
char *s = argv[argc]+1;
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
fmtlist[i].disabled = 1;
|
|
}
|
|
for (s = strtok(s,","); s; s = strtok(NULL,",")) {
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
const char *t = fmtlist[i].name;
|
|
int l;
|
|
while (*t == ' ')
|
|
t++;
|
|
l = strlen(t);
|
|
while (l > 1 && t[l-1] == ' ')
|
|
l--;
|
|
if (strlen(s) == l && memcmp(s,t,l) == 0) {
|
|
fmtlist[i].disabled = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (fmtlist[i].fmt == IMG_NONE) {
|
|
fprintf(stderr, "Unknown image format `%s'\n", s);
|
|
fprintf(stderr, "`%s -h' for help.\n", argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
} else if (argv[argc][0] == '@') {
|
|
if (sscanf(argv[argc]+1, "%dx%d", &width, &height) != 2
|
|
|| width <= 0 || height <= 0
|
|
) {
|
|
fprintf(stderr, "Invalid image size `%s'\n", argv[argc]+1);
|
|
fprintf(stderr, "`%s -h' for help.\n", argv[0]);
|
|
return 1;
|
|
}
|
|
if (width > WIDTH || height > HEIGHT) {
|
|
fprintf(stderr, "Image size too large (max %dx%d)\n",
|
|
WIDTH, HEIGHT);
|
|
fprintf(stderr, "`%s -h' for help.\n", argv[0]);
|
|
return 1;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Unknown accel type `%s'\n", argv[argc]);
|
|
fprintf(stderr, "`%s -h' for help.\n", argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
if (accel) {
|
|
if (accel & ~ac_cpuinfo()) {
|
|
fprintf(stderr, "Unavailable accel type(s):%s\n",
|
|
accel_flags(accel & ~ac_cpuinfo()));
|
|
fprintf(stderr, "Supported on this machine:%s\n",
|
|
accel_flags(ac_cpuinfo()));
|
|
return 1;
|
|
}
|
|
} else {
|
|
accel = ac_cpuinfo();
|
|
}
|
|
|
|
srandom(0); /* to give a standard "image" */
|
|
for (i = 0; i < sizeof(srcbuf); i++)
|
|
srcbuf[i] = random();
|
|
|
|
if (check) {
|
|
if (ac_cpuinfo() & (AC_IA32ASM | AC_AMD64ASM)) {
|
|
if (!checkall(srcbuf, AC_IA32ASM | AC_AMD64ASM,
|
|
verbose ? "asm" : NULL))
|
|
return 1;
|
|
}
|
|
if (ac_cpuinfo() & AC_MMX) {
|
|
if (!checkall(srcbuf, AC_IA32ASM | AC_AMD64ASM | AC_MMX,
|
|
verbose ? "mmx" : NULL))
|
|
return 1;
|
|
}
|
|
if (ac_cpuinfo() & AC_SSE2) {
|
|
if (!checkall(srcbuf, AC_IA32ASM | AC_AMD64ASM | AC_CMOVE
|
|
| AC_MMX | AC_SSE | AC_SSE2,
|
|
verbose ? "sse2" : NULL))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
printf("Acceleration flags:%s\n", accel_flags(accel));
|
|
if (compare)
|
|
printf("Units: conversions/time (unaccelerated = 100)\n\n");
|
|
else
|
|
printf("Units: conversions/sec (frame size: %dx%d)\n\n", width, height);
|
|
printf(" |");
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
if (!fmtlist[i].disabled)
|
|
printf("%-4s|", fmtlist[i].name);
|
|
}
|
|
printf("\n----+");
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
if (!fmtlist[i].disabled)
|
|
printf("----+");
|
|
}
|
|
printf("\n");
|
|
|
|
for (i = 0; fmtlist[i].fmt != IMG_NONE; i++) {
|
|
if (fmtlist[i].disabled)
|
|
continue;
|
|
printf("%-4s|", fmtlist[i].name);
|
|
fflush(stdout);
|
|
for (j = 0; fmtlist[j].fmt != IMG_NONE; j++) {
|
|
if (fmtlist[j].disabled)
|
|
continue;
|
|
int res = testit(srcbuf, fmtlist[i].fmt, fmtlist[j].fmt,
|
|
width, height, accel, verbose, 0);
|
|
switch (res) {
|
|
case -1:
|
|
case -2:
|
|
case -3:
|
|
case -4:
|
|
case -5: printf("----|"); break;
|
|
case -6: printf("BAD |"); break;
|
|
case -7: printf("SEGV|"); break;
|
|
case -8: printf("ILL |"); break;
|
|
default:
|
|
if (compare) {
|
|
int res0 = testit(srcbuf, fmtlist[i].fmt,
|
|
fmtlist[j].fmt, width, height,
|
|
0, 0, 0);
|
|
if (res0 < 0)
|
|
printf("****|");
|
|
else
|
|
printf("%4d|", (100*res0 + res/2) / res);
|
|
} else {
|
|
printf("%4d|", (1000000+res/2)/res);
|
|
}
|
|
break;
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|