Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: "Martin Storsjö" <martin@martin.st>
To: "Swinney, Jonathan" <jswinney@amazon.com>
Cc: "J. Dekker" <jdek@itanimul.li>,
	"Pop, Sebastian" <spop@amazon.com>,
	"ffmpeg-devel@ffmpeg.org" <ffmpeg-devel@ffmpeg.org>
Subject: Re: [FFmpeg-devel] [PATCH 1/2] checkasm: updated tests for sw_scale
Date: Wed, 22 Jun 2022 12:22:58 +0300 (EEST)
Message-ID: <a4f2c340-b44-8a5-8558-90f8f2aae153@martin.st> (raw)
In-Reply-To: <4a2e77c6-1ce4-74b6-583a-e671f1efc1aa@martin.st>

[-- Attachment #1: Type: text/plain, Size: 1508 bytes --]

On Tue, 21 Jun 2022, Martin Storsjö wrote:

> On Mon, 13 Jun 2022, Swinney, Jonathan wrote:
>
>> - added a test for yuv2plane1 (currently disabled for x86_64)
>
> What's the reason for having it disabled for x86 - is it another case where 
> the current implementations there aren't bitexact? Could we avoid that by 
> setting the bitexact flag for the new yuv2yuv1 test?
>
>> - fixed test for yuv2planeX for aarch64 which was previously not working at 
>> all
>
> Could we make the test fuzzy and allow minor differences from the reference, 
> when the bitexact flag isn't set, and separately test with the bitexact flag 
> and require exact matches?
>
>> @@ -95,7 +210,7 @@ static void check_yuv2yuvX(void)
>>     ff_sws_init_scale(ctx);
>>     for(isi = 0; isi < INPUT_SIZES; ++isi){
>>         dstW = input_sizes[isi];
>> -        for(osi = 0; osi < 64; osi += 16){
>> +        for(osi = 0; osi < 1; osi += 16){
>
> This looks like a stray leftover change?

I had a look at this, trying to fix things up. This now passes tests on 
x86_32, x86_64 and aarch64. See the attached patch, which goes on top of 
yours.

It's not intended as a final version of how things should be necessarily, 
but as a more concrete pointer about how it could be done - it needs at 
least reindenting after adding the outer for loop.

I also had to skip the filter sizes 1 and 3 in check_yuv2yuvX, because 
ff_yuv2planeX_8_sse2 couldn't handle those. I presume that means that in 
practice, those aren't ever used?

// Martin

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-diff; name=0001-checkasm-sw_scale-Fixups.patch, Size: 13386 bytes --]

From e6d33cc199a879060e8e954e6eaea8aebe96b433 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
Date: Wed, 22 Jun 2022 12:16:15 +0300
Subject: [PATCH] checkasm: sw_scale: Fixups

Changes:
- Omit x86 yuv2plane1 if SWS_ACCURATE_RND is set
- Remove yuv2plane1_8_ref from the testcase; this function can
  be tested with the usual call_ref pattern
- Add testing with and without SWS_ACCURATE_RND in both check_yuv2yuvX
  and check_yuv2yuv1
- Remove the x86 specific reference function in checkasm; use the
  generic reference function which matches swscale's C code, and
  compare with a tolerance of 2 unless SWS_ACCURATE_RND is set
- Reinstate testing of offsets in check_yuv2yuvX
- Make all tests arch independent
- Clarify why we can't use call_ref in check_yuv2yuvX

The code isn't properly indented (I added an outer for loop, without
reindenting the loop body, for patch clarity).
---
 libswscale/x86/swscale.c  |  11 ++--
 tests/checkasm/sw_scale.c | 113 +++++++++++++++-----------------------
 2 files changed, 50 insertions(+), 74 deletions(-)

diff --git a/libswscale/x86/swscale.c b/libswscale/x86/swscale.c
index 73869355b8..1c57b3a700 100644
--- a/libswscale/x86/swscale.c
+++ b/libswscale/x86/swscale.c
@@ -550,7 +550,8 @@ switch(c->dstBpc){ \
     if (EXTERNAL_MMX(cpu_flags)) {
         ASSIGN_MMX_SCALE_FUNC(c->hyScale, c->hLumFilterSize, mmx, mmx);
         ASSIGN_MMX_SCALE_FUNC(c->hcScale, c->hChrFilterSize, mmx, mmx);
-        ASSIGN_VSCALE_FUNC(c->yuv2plane1, mmx, mmxext, cpu_flags & AV_CPU_FLAG_MMXEXT);
+        if (!(c->flags & SWS_ACCURATE_RND))
+            ASSIGN_VSCALE_FUNC(c->yuv2plane1, mmx, mmxext, cpu_flags & AV_CPU_FLAG_MMXEXT);
 
         switch (c->srcFormat) {
         case AV_PIX_FMT_YA8:
@@ -599,7 +600,8 @@ switch(c->dstBpc){ \
         ASSIGN_SSE_SCALE_FUNC(c->hcScale, c->hChrFilterSize, sse2, sse2);
         ASSIGN_VSCALEX_FUNC(c->yuv2planeX, sse2, ,
                             HAVE_ALIGNED_STACK || ARCH_X86_64);
-        ASSIGN_VSCALE_FUNC(c->yuv2plane1, sse2, sse2, 1);
+        if (!(c->flags & SWS_ACCURATE_RND))
+            ASSIGN_VSCALE_FUNC(c->yuv2plane1, sse2, sse2, 1);
 
         switch (c->srcFormat) {
         case AV_PIX_FMT_YA8:
@@ -648,14 +650,15 @@ switch(c->dstBpc){ \
         ASSIGN_VSCALEX_FUNC(c->yuv2planeX, sse4,
                             if (!isBE(c->dstFormat)) c->yuv2planeX = ff_yuv2planeX_16_sse4,
                             HAVE_ALIGNED_STACK || ARCH_X86_64);
-        if (c->dstBpc == 16 && !isBE(c->dstFormat))
+        if (c->dstBpc == 16 && !isBE(c->dstFormat) && !(c->flags & SWS_ACCURATE_RND))
             c->yuv2plane1 = ff_yuv2plane1_16_sse4;
     }
 
     if (EXTERNAL_AVX(cpu_flags)) {
         ASSIGN_VSCALEX_FUNC(c->yuv2planeX, avx, ,
                             HAVE_ALIGNED_STACK || ARCH_X86_64);
-        ASSIGN_VSCALE_FUNC(c->yuv2plane1, avx, avx, 1);
+        if (!(c->flags & SWS_ACCURATE_RND))
+            ASSIGN_VSCALE_FUNC(c->yuv2plane1, avx, avx, 1);
 
         switch (c->srcFormat) {
         case AV_PIX_FMT_YUYV422:
diff --git a/tests/checkasm/sw_scale.c b/tests/checkasm/sw_scale.c
index edce3cbe4e..0c19974034 100644
--- a/tests/checkasm/sw_scale.c
+++ b/tests/checkasm/sw_scale.c
@@ -39,45 +39,25 @@ static void yuv2planeX_8_ref(const int16_t *filter, int filterSize,
                              const int16_t **src, uint8_t *dest, int dstW,
                              const uint8_t *dither, int offset)
 {
-#if ARCH_X86_64
-    // This reference function is the same approximate algorithm employed by the
-    // SIMD functions on x86.
-    int i, d;
-    d = ((filterSize - 1) * 8 + dither[0]) >> 4;
-    for ( i = 0; i < dstW; i++) {
-        int16_t val = d;
-        int j;
-        union {
-            int val;
-            int16_t v[2];
-        } t;
-        for (j = 0; j < filterSize; j++){
-            t.val = (int)src[j][i + offset] * (int)filter[j];
-            val += t.v[1];
-        }
-        dest[i]= av_clip_uint8(val>>3);
-    }
-#else
-    // Other architectures use the default implementation as the reference.
+    // This corresponds to the yuv2planeX_8_c function
     int i;
-    for (i=0; i<dstW; i++) {
+    for (i = 0; i < dstW; i++) {
         int val = dither[(i + offset) & 7] << 12;
         int j;
-        for (j=0; j<filterSize; j++)
+        for (j = 0; j < filterSize; j++)
             val += src[j][i] * filter[j];
 
-        dest[i]= av_clip_uint8(val>>19);
+        dest[i]= av_clip_uint8(val >> 19);
     }
-#endif
 }
-static void yuv2plane1_8_ref(const int16_t *src, uint8_t *dest, int dstW,
-                             const uint8_t *dither, int offset)
+
+static int cmp_off_by_n(const uint8_t *ref, const uint8_t *test, size_t n, int accuracy)
 {
-    int i;
-    for (i=0; i<dstW; i++) {
-        int val = (src[i] + dither[(i + offset) & 7]) >> 7;
-        dest[i]= av_clip_uint8(val);
+    for (size_t i = 0; i < n; i++) {
+        if (abs(ref[i] - test[i]) > accuracy)
+            return 1;
     }
+    return 0;
 }
 
 static void print_data(uint8_t *p, size_t len, size_t offset)
@@ -85,7 +65,7 @@ static void print_data(uint8_t *p, size_t len, size_t offset)
     size_t i = 0;
     for (; i < len; i++) {
         if (i % 8 == 0) {
-            printf("0x%04lx: ", i+offset);
+            printf("0x%04zx: ", i+offset);
         }
         printf("0x%02x ", (uint32_t) p[i]);
         if (i % 8 == 7) {
@@ -140,7 +120,10 @@ static void check_yuv2yuv1(void)
 
     randomize_buffers((uint8_t*)dither, 8);
     randomize_buffers((uint8_t*)src_pixels, LARGEST_INPUT_SIZE * sizeof(int16_t));
+  for (int accurate = 0; accurate <= 1; accurate++) {
     ctx = sws_alloc_context();
+    if (accurate)
+        ctx->flags |= SWS_ACCURATE_RND;
     if (sws_init_context(ctx, NULL, NULL) < 0)
         fail();
 
@@ -149,13 +132,13 @@ static void check_yuv2yuv1(void)
         dstW = input_sizes[isi];
         for(osi = 0; osi < OFFSET_SIZES; osi++){
             offset = offsets[osi];
-            if (check_func(ctx->yuv2plane1, "yuv2yuv1_%d_%d", offset, dstW)){
+            if (check_func(ctx->yuv2plane1, "yuv2yuv1_%d_%d%s", offset, dstW, accurate ? "_accurate" : "")){
                 memset(dst0, 0, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
                 memset(dst1, 0, LARGEST_INPUT_SIZE * sizeof(dst1[0]));
 
-                yuv2plane1_8_ref(src_pixels, dst0, dstW, dither, offset);
+                call_ref(src_pixels, dst0, dstW, dither, offset);
                 call_new(src_pixels, dst1, dstW, dither, offset);
-                if (memcmp(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]))) {
+                if (cmp_off_by_n(dst0, dst1, dstW * sizeof(dst0[0]), accurate ? 0 : 2)) {
                     fail();
                     printf("failed: yuv2yuv1_%d_%d\n", offset, dstW);
                     fail_offset = show_differences(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
@@ -171,6 +154,7 @@ static void check_yuv2yuv1(void)
         }
     }
     sws_freeContext(ctx);
+  }
 }
 
 static void check_yuv2yuvX(void)
@@ -179,7 +163,8 @@ static void check_yuv2yuvX(void)
     int fsi, osi, isi, i, j;
     int dstW;
 #define LARGEST_FILTER 16
-    const int filter_sizes[] = {1, 2, 3, 4, 8, 16};
+    // ff_yuv2planeX_8_sse2 can't handle odd filter sizes
+    const int filter_sizes[] = {2, 4, 8, 16};
     const int FILTER_SIZES = sizeof(filter_sizes)/sizeof(filter_sizes[0]);
 #define LARGEST_INPUT_SIZE 512
     static const int input_sizes[] = {8, 24, 128, 144, 256, 512};
@@ -203,58 +188,47 @@ static void check_yuv2yuvX(void)
     memset(dither, d_val, LARGEST_INPUT_SIZE);
     randomize_buffers((uint8_t*)src_pixels, LARGEST_FILTER * LARGEST_INPUT_SIZE * sizeof(int16_t));
     randomize_buffers((uint8_t*)filter_coeff, LARGEST_FILTER * sizeof(int16_t));
+  for (int accurate = 0; accurate <= 1; accurate++) {
     ctx = sws_alloc_context();
+    if (accurate)
+        ctx->flags |= SWS_ACCURATE_RND;
     if (sws_init_context(ctx, NULL, NULL) < 0)
         fail();
 
     ff_sws_init_scale(ctx);
     for(isi = 0; isi < INPUT_SIZES; ++isi){
         dstW = input_sizes[isi];
-        for(osi = 0; osi < 1; osi += 16){
+        for(osi = 0; osi < 64; osi += 16){
+            if (dstW <= osi)
+                continue;
             for(fsi = 0; fsi < FILTER_SIZES; ++fsi){
                 src = av_malloc(sizeof(int16_t*) * filter_sizes[fsi]);
                 vFilterData = av_malloc((filter_sizes[fsi] + 2) * sizeof(union VFilterData));
                 memset(vFilterData, 0, (filter_sizes[fsi] + 2) * sizeof(union VFilterData));
                 for(i = 0; i < filter_sizes[fsi]; ++i){
                     src[i] = &src_pixels[i * LARGEST_INPUT_SIZE];
-                    vFilterData[i].src = src[i];
+                    vFilterData[i].src = src[i] - osi;
                     for(j = 0; j < 4; ++j)
                         vFilterData[i].coeff[j + 4] = filter_coeff[i];
                 }
-                if (check_func(ctx->yuv2planeX, "yuv2yuvX_%d_%d_%d", filter_sizes[fsi], osi, dstW)){
+                if (check_func(ctx->yuv2planeX, "yuv2yuvX_%d_%d_%d%s", filter_sizes[fsi], osi, dstW, accurate ? "_accurate" : "")){
+                    const int16_t *filter = ctx->use_mmx_vfilter ? (const int16_t*)vFilterData : &filter_coeff[0];
                     memset(dst0, 0, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
                     memset(dst1, 0, LARGEST_INPUT_SIZE * sizeof(dst1[0]));
 
-                    if (ARCH_X86_64) {
-                        // The reference function is not the scalar function selected when mmx
-                        // is deactivated as the SIMD functions do not give the same result as
-                        // the scalar ones due to rounding. The SIMD functions are activated by
-                        // the flag SWS_ACCURATE_RND
-                        yuv2planeX_8_ref(&filter_coeff[0], filter_sizes[fsi], src, dst0, dstW - osi, dither, osi);
-                        // There's no point in calling new for the reference function
-                        if(ctx->use_mmx_vfilter) {
-                            call_new((const int16_t*)vFilterData, filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
-                            if (memcmp(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]))) {
-                                fail();
-                                printf("failed: yuv2yuvX_%d_%d_%d\n", filter_sizes[fsi], osi, dstW);
-                                show_differences(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
-                            }
-                            if(dstW == LARGEST_INPUT_SIZE)
-                                bench_new((const int16_t*)vFilterData, filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
-                        }
-                    }
+                    // We can't use call_ref here, because we don't know if use_mmx_vfilter was set for that
+                    // function or not, so we can't pass it the parameters correctly.
+                    yuv2planeX_8_ref(&filter_coeff[0], filter_sizes[fsi], src, dst0, dstW - osi, dither, osi);
 
-                    if (ARCH_AARCH64) {
-                        yuv2planeX_8_ref(&filter_coeff[0], filter_sizes[fsi], src, dst0, dstW - osi, dither, osi);
-                        call_new(&filter_coeff[0], filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
-                        if (memcmp(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]))) {
-                            fail();
-                            printf("failed: yuv2yuvX_%d_%d_%d\n", filter_sizes[fsi], osi, dstW);
-                            show_differences(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
-                        }
-                        if(dstW == LARGEST_INPUT_SIZE)
-                            bench_new(&filter_coeff[0], filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
+                    call_new(filter, filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
+                    if (cmp_off_by_n(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]), accurate ? 0 : 2)) {
+                        fail();
+                        printf("failed: yuv2yuvX_%d_%d_%d%s\n", filter_sizes[fsi], osi, dstW, accurate ? "_accurate" : "");
+                        show_differences(dst0, dst1, LARGEST_INPUT_SIZE * sizeof(dst0[0]));
                     }
+                    if(dstW == LARGEST_INPUT_SIZE)
+                        bench_new((const int16_t*)vFilterData, filter_sizes[fsi], src, dst1, dstW - osi, dither, osi);
+
                 }
                 av_freep(&src);
                 av_freep(&vFilterData);
@@ -262,6 +236,7 @@ static void check_yuv2yuvX(void)
         }
     }
     sws_freeContext(ctx);
+  }
 #undef FILTER_SIZES
 }
 
@@ -377,10 +352,8 @@ void checkasm_check_sw_scale(void)
 {
     check_hscale();
     report("hscale");
-    if (!ARCH_X86_64) {
-        check_yuv2yuv1();
-        report("yuv2yuv1");
-    }
+    check_yuv2yuv1();
+    report("yuv2yuv1");
     check_yuv2yuvX();
     report("yuv2yuvX");
 }
-- 
2.32.0 (Apple Git-132)


[-- Attachment #3: Type: text/plain, Size: 251 bytes --]

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

      reply	other threads:[~2022-06-22  9:23 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-13 16:36 Swinney, Jonathan
2022-06-21 20:16 ` Martin Storsjö
2022-06-22  9:22   ` Martin Storsjö [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a4f2c340-b44-8a5-8558-90f8f2aae153@martin.st \
    --to=martin@martin.st \
    --cc=ffmpeg-devel@ffmpeg.org \
    --cc=jdek@itanimul.li \
    --cc=jswinney@amazon.com \
    --cc=spop@amazon.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git