From 0cef06df073934ca08d0357fcbbbcf2bc9b2a0cd Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Fri, 31 Jul 2015 12:37:14 +0200 Subject: [PATCH] checkasm: add HEVC MC tests --- tests/checkasm/Makefile | 1 + tests/checkasm/checkasm.c | 3 + tests/checkasm/checkasm.h | 1 + tests/checkasm/hevc_mc.c | 320 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 tests/checkasm/hevc_mc.c diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile index 5fccad9866..75c9a188f8 100644 --- a/tests/checkasm/Makefile +++ b/tests/checkasm/Makefile @@ -2,6 +2,7 @@ AVCODECOBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o AVCODECOBJS-$(CONFIG_H264PRED) += h264pred.o AVCODECOBJS-$(CONFIG_H264QPEL) += h264qpel.o +AVCODECOBJS-$(CONFIG_HEVC_DECODER) += hevc_mc.o AVCODECOBJS-$(CONFIG_V210_ENCODER) += v210enc.o CHECKASMOBJS-$(CONFIG_AVCODEC) += $(AVCODECOBJS-yes) diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c index 9219a83111..70627717a4 100644 --- a/tests/checkasm/checkasm.c +++ b/tests/checkasm/checkasm.c @@ -66,6 +66,9 @@ static const struct { #if CONFIG_H264QPEL { "h264qpel", checkasm_check_h264qpel }, #endif +#if CONFIG_HEVC_DECODER + { "hevc_mc", checkasm_check_hevc_mc }, +#endif #if CONFIG_V210_ENCODER { "v210enc", checkasm_check_v210enc }, #endif diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index aa32655ad4..c1206e7f1e 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -32,6 +32,7 @@ void checkasm_check_bswapdsp(void); void checkasm_check_h264pred(void); void checkasm_check_h264qpel(void); +void checkasm_check_hevc_mc(void); void checkasm_check_v210enc(void); void *checkasm_check_func(void *func, const char *name, ...) av_printf_format(2, 3); diff --git a/tests/checkasm/hevc_mc.c b/tests/checkasm/hevc_mc.c new file mode 100644 index 0000000000..7139369b1d --- /dev/null +++ b/tests/checkasm/hevc_mc.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2015 Anton Khirnov + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with Libav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "checkasm.h" + +#include "libavcodec/avcodec.h" +#include "libavcodec/hevcdsp.h" + +#include "libavutil/common.h" +#include "libavutil/intreadwrite.h" + +// max PU size + interpolation stencil +#define BUF_SIZE (FFALIGN(64 + 7, 16) * (64 + 7) * 2) + +#define PIXEL_SIZE(depth) ((depth + 7) / 8) + +#define randomize_buffers(buf, size, depth) \ + do { \ + uint32_t mask = pixel_mask[depth - 8]; \ + int i; \ + for (i = 0; i < size; i += 4) { \ + uint32_t r = rnd() & mask; \ + AV_WN32A(buf + i, r); \ + } \ + } while (0) + +static const uint32_t pixel_mask[3] = { 0xffffffff, 0x01ff01ff, 0x03ff03ff }; + +static const int pred_heights[][7] = { + [2] = { 8, 4, 2, 0 }, + [4] = { 16, 8, 4, 2, 0 }, + [6] = { 8, 0 }, + [8] = { 32, 16, 8, 4, 2, 0 }, + [12] = { 16, 0 }, + [16] = { 64, 32, 16, 12, 8, 4, 0 }, + [24] = { 32, 0 }, + [32] = { 64, 32, 24, 16, 8, 0 }, + [48] = { 64, 0 }, + [64] = { 64, 48, 32, 16, 0 }, +}; + +static const int pred_widths[] = { 4, 8, 12, 16, 24, 32, 48, 64 }; + +static const char *interp_names[2][2] = { { "pixels", "h" }, { "v", "hv" } }; + +#define UNWEIGHTED_PRED(dst0, dst1, src0, width, bit_depth) \ +do { \ + int i; \ + for (i = 0; i < FF_ARRAY_ELEMS(pred_heights[i]); i++) { \ + int height = pred_heights[width][i]; \ + if (!height) \ + break; \ + call_ref(dst0, dststride, src0, srcstride, height); \ + call_new(dst1, dststride, src0, srcstride, height); \ + if (memcmp(dst0, dst1, dststride * height)) \ + fail(); \ + bench_new(dst1, dststride, src0, srcstride, height); \ + } \ +} while (0) + +#define UNWEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth) \ +do { \ + int i; \ + for (i = 0; i < FF_ARRAY_ELEMS(pred_heights[i]); i++) { \ + int height = pred_heights[width][i]; \ + if (!height) \ + break; \ + call_ref(dst0, dststride, src0, src1, srcstride, height); \ + call_new(dst1, dststride, src0, src1, srcstride, height); \ + if (memcmp(dst0, dst1, dststride * height)) \ + fail(); \ + bench_new(dst1, dststride, src0, src1, srcstride, height); \ + } \ +} while (0) + +static void check_unweighted_pred(HEVCDSPContext *h, uint8_t *dst0, uint8_t *dst1, + int16_t *src0, int16_t *src1, int bit_depth) +{ + int i; + + randomize_buffers(src0, BUF_SIZE, 8); + randomize_buffers(src1, BUF_SIZE, 8); + + memset(dst0, 0, BUF_SIZE * sizeof(*dst0)); + memset(dst1, 0, BUF_SIZE * sizeof(*dst1)); + + for (i = 0; i < FF_ARRAY_ELEMS(pred_widths); i++) { + const int width = pred_widths[i]; + const int srcstride = FFALIGN(width, 16) * sizeof(*src0); + const int dststride = FFALIGN(width, 16) * PIXEL_SIZE(bit_depth); + + { + declare_func(void, uint8_t *dst, ptrdiff_t dststride, int16_t *src, ptrdiff_t srcstride, int height); + if (check_func(h->put_unweighted_pred[i], "put_unweighted_pred_%d_%d", width, bit_depth)) + UNWEIGHTED_PRED(dst0, dst1, src0, width, bit_depth); + if (check_func(h->put_unweighted_pred_chroma[i], "put_unweighted_pred_%d_%d", width / 2, bit_depth)) + UNWEIGHTED_PRED(dst0, dst1, src0, width, bit_depth); + } + { + declare_func(void, uint8_t *dst, ptrdiff_t dststride, + int16_t *src0, int16_t *src1, ptrdiff_t srcstride, int height); + if (check_func(h->put_unweighted_pred_avg[i], "put_unweighted_pred_avg_%d_%d", width, bit_depth)) + UNWEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth); + if (check_func(h->put_unweighted_pred_avg_chroma[i], "put_unweighted_pred_avg_%d_%d", width / 2, bit_depth)) + UNWEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth); + } + } +} + +#define WEIGHTED_PRED(dst0, dst1, src0, width, bit_depth) \ +do { \ + int i; \ + for (i = 0; i < FF_ARRAY_ELEMS(pred_heights[i]); i++) { \ + int height = pred_heights[width][i]; \ + if (!height) \ + break; \ + call_ref(denom, weight0, offset0, dst0, dststride, src0, srcstride, height); \ + call_new(denom, weight0, offset0, dst1, dststride, src0, srcstride, height); \ + if (memcmp(dst0, dst1, dststride * height)) \ + fail(); \ + bench_new(denom, weight0, offset0, dst1, dststride, src0, srcstride, height); \ + } \ +} while (0) + +#define WEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth) \ +do { \ + int i; \ + for (i = 0; i < FF_ARRAY_ELEMS(pred_heights[i]); i++) { \ + int height = pred_heights[width][i]; \ + if (!height) \ + break; \ + call_ref(denom, weight0, weight1, offset0, offset1, dst0, dststride, src0, src1, srcstride, height); \ + call_new(denom, weight0, weight1, offset0, offset1, dst1, dststride, src0, src1, srcstride, height); \ + if (memcmp(dst0, dst1, dststride * height)) \ + fail(); \ + bench_new(denom, weight0, weight1, offset0, offset1, dst1, dststride, src0, src1, srcstride, height); \ + } \ +} while (0) + +static void check_weighted_pred(HEVCDSPContext *h, uint8_t *dst0, uint8_t *dst1, + int16_t *src0, int16_t *src1, int bit_depth) +{ + uint8_t denom; + int16_t weight0, weight1, offset0, offset1; + int i; + + randomize_buffers(src0, BUF_SIZE, 8); + randomize_buffers(src1, BUF_SIZE, 8); + + denom = rnd() & 7; + weight0 = denom + ((rnd() & 255) - 128); + weight1 = denom + ((rnd() & 255) - 128); + offset0 = (rnd() & 255) - 128; + offset1 = (rnd() & 255) - 128; + + memset(dst0, 0, BUF_SIZE * sizeof(*dst0)); + memset(dst1, 0, BUF_SIZE * sizeof(*dst1)); + + for (i = 0; i < FF_ARRAY_ELEMS(pred_widths); i++) { + const int width = pred_widths[i]; + const int srcstride = FFALIGN(width, 16) * sizeof(*src0); + const int dststride = FFALIGN(width, 16) * PIXEL_SIZE(bit_depth); + + { + declare_func(void, uint8_t denom, int16_t weight, int16_t offset, + uint8_t *dst, ptrdiff_t dststride, int16_t *src, ptrdiff_t srcstride, int height); + if (check_func(h->weighted_pred[i], "weighted_pred_%d_%d", width, bit_depth)) + WEIGHTED_PRED(dst0, dst1, src0, width, bit_depth); + if (check_func(h->weighted_pred_chroma[i], "weighted_pred_%d_%d", width / 2, bit_depth)) + WEIGHTED_PRED(dst0, dst1, src0, width, bit_depth); + } + { + declare_func(void, uint8_t denom, int16_t weight0, int16_t weight1, int16_t offset0, int16_t offset1, + uint8_t *dst, ptrdiff_t dststride, int16_t *src0, int16_t *src1, ptrdiff_t srcstride, int height); + if (check_func(h->weighted_pred_avg[i], "weighted_pred_avg_%d_%d", width, bit_depth)) + WEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth); + if (check_func(h->weighted_pred_avg_chroma[i], "weighted_pred_avg_%d_%d", width / 2, bit_depth)) + WEIGHTED_PRED_AVG(dst0, dst1, src0, src1, width, bit_depth); + } + } +} + +static void check_epel(HEVCDSPContext *h, int16_t *dst0, int16_t *dst1, + uint8_t *src, int16_t *mcbuffer, int bit_depth) +{ + int i, j, k, l, mx, my; + + declare_func(void, int16_t *dst, ptrdiff_t dststride, uint8_t *src, ptrdiff_t srcstride, + int height, int mx, int my, int16_t *mcbuffer); + + randomize_buffers(src, BUF_SIZE, bit_depth); + + memset(dst0, 0, BUF_SIZE * sizeof(*dst0)); + memset(dst1, 0, BUF_SIZE * sizeof(*dst1)); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < FF_ARRAY_ELEMS(h->put_hevc_epel[i][j]); k++) { + int width = pred_widths[k] / 2; + int dststride = FFALIGN(width, 16) * sizeof(*dst0); + int srcstride = FFALIGN(width + 3, 8) * PIXEL_SIZE(bit_depth); + + if (!check_func(h->put_hevc_epel[i][j][k], "epel_%s_%d_%d", interp_names[i][j], width, bit_depth)) + continue; + + for (l = 0; l < FF_ARRAY_ELEMS(pred_heights[0]); l++) { + int height = pred_heights[width][l]; + + if (!height) + continue; + + for (my = i; my < (i ? 8 : 1); my++) + for (mx = j; mx < (j ? 8 : 1); mx++) { + call_ref(dst0, dststride, src + srcstride + PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + call_new(dst1, dststride, src + srcstride + PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + + if (memcmp(dst0, dst1, dststride * height * sizeof(*dst0))) + fail(); + + bench_new(dst1, dststride, src + srcstride + PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + } + } + } + } + } +} + +static void check_qpel(HEVCDSPContext *h, int16_t *dst0, int16_t *dst1, + uint8_t *src, int16_t *mcbuffer, int bit_depth) +{ + int i, j, k, l, mx, my; + + declare_func(void, int16_t *dst, ptrdiff_t dststride, uint8_t *src, ptrdiff_t srcstride, + int height, int mx, int my, int16_t *mcbuffer); + + randomize_buffers(src, BUF_SIZE, bit_depth); + + memset(dst0, 0, BUF_SIZE * sizeof(*dst0)); + memset(dst1, 0, BUF_SIZE * sizeof(*dst1)); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < FF_ARRAY_ELEMS(h->put_hevc_qpel[i][j]); k++) { + int width = pred_widths[k]; + int dststride = FFALIGN(width, 16) * sizeof(*dst0); + int srcstride = FFALIGN(width + 7, 8) * PIXEL_SIZE(bit_depth); + + if (!check_func(h->put_hevc_qpel[i][j][k], "qpel_%s_%d_%d", interp_names[i][j], width, bit_depth)) + continue; + + for (l = 0; l < FF_ARRAY_ELEMS(pred_heights[0]); l++) { + int height = pred_heights[width][l]; + + if (!height) + continue; + + for (my = i; my < (i ? 2 : 1); my++) + for (mx = j; mx < (j ? 2 : 1); mx++) { + call_ref(dst0, dststride, src + 3 * srcstride + 3 * PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + call_new(dst1, dststride, src + 3 * srcstride + 3 * PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + + if (memcmp(dst0, dst1, dststride * height * sizeof(*dst0))) + fail(); + + bench_new(dst1, dststride, src + 3 * srcstride + 3 * PIXEL_SIZE(bit_depth), srcstride, height, mx, my, mcbuffer); + } + } + } + } + } +} + +void checkasm_check_hevc_mc(void) +{ + DECLARE_ALIGNED(16, uint8_t, buf8_0)[BUF_SIZE]; + DECLARE_ALIGNED(16, uint8_t, buf8_1)[BUF_SIZE]; + + DECLARE_ALIGNED(16, int16_t, buf16_0)[BUF_SIZE]; + DECLARE_ALIGNED(16, int16_t, buf16_1)[BUF_SIZE]; + + DECLARE_ALIGNED(16, int16_t, mcbuffer)[BUF_SIZE]; + + HEVCDSPContext h; + int bit_depth; + + for (bit_depth = 8; bit_depth <= 10; bit_depth++) { + ff_hevc_dsp_init(&h, bit_depth); + + check_qpel(&h, buf16_0, buf16_1, buf8_0, mcbuffer, bit_depth); + report("qpel"); + + check_epel(&h, buf16_0, buf16_1, buf8_0, mcbuffer, bit_depth); + report("epel"); + + check_unweighted_pred(&h, buf8_0, buf8_1, buf16_0, buf16_1, bit_depth); + report("unweighted_pred"); + + check_weighted_pred(&h, buf8_0, buf8_1, buf16_0, buf16_1, bit_depth); + report("weighted_pred"); + } +}