PR #22441 opened by Ramiro Polla (ramiro)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22441
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22441.patch

The main improvement is:

```
swscale/tests/swscale: require reference file to perform comparisons

The legacy scaler is no longer implicitly used to generate a reference
to perform comparisons for every conversion. It is now up to the user
to generate a reference file and use it as input for a separate run to
perform comparisons.

It is now possible to compare against previous runs of the graph-based
scaler, for example to test for newer optimizations.

This reduces the overall time necessary to obtain speedup numbers from
the legacy scaler to the graph-based scaler (or any other comparison,
for that matter) since the reference must only be run once.

For example, to check the speedup between the legacy scaler and the
graph-based scaler:
  ./libswscale/tests/swscale [...] -bench 50 -legacy 1 > legacy_ref.txt
  ./libswscale/tests/swscale [...] -bench 50 -ref legacy_ref.txt

If no -ref file is specified, we are assuming that we are generating a
reference file, and therefore all information is printed (including
ssim/loss, and benchmarks if -bench is used).

If a -ref file is specified, the output printed depends on whether we
are testing for correctness (ssim/loss only) or benchmarking (time/
speedup only, along with overall speedup).
```



>From ab5ca1140256734ebb59ebbae6d714624748e347 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 18 Feb 2026 15:07:47 +0100
Subject: [PATCH 01/31] tests/swscale: exclude init time from benchmark

This was originally intended to also include performance gains/losses
due to complicated setup logic, but in practice it just means that changing
the number of iterations dramatically affects the measured speedup; which
makes it harder to do quick bench runs during development.
---
 libswscale/tests/swscale.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 6f6240e4b6..63f566ba18 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -193,7 +193,7 @@ static float get_loss(const float ssim[4])
 }
 
 static int scale_legacy(AVFrame *dst, const AVFrame *src, struct mode mode,
-                        struct options opts)
+                        struct options opts, int64_t *out_time)
 {
     SwsContext *sws_legacy;
     int ret;
@@ -215,8 +215,10 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src, 
struct mode mode,
     if ((ret = sws_init_context(sws_legacy, NULL, NULL)) < 0)
         goto error;
 
+    int64_t time = av_gettime_relative();
     for (int i = 0; ret >= 0 && i < opts.iters; i++)
         ret = sws_scale_frame(sws_legacy, dst, src);
+    *out_time = av_gettime_relative() - time;
 
 error:
     sws_freeContext(sws_legacy);
@@ -273,6 +275,12 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     sws[1]->dither = mode.dither;
     sws[1]->threads = opts.threads;
 
+    if (sws_frame_setup(sws[1], dst, src) < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
+               av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+        goto error;
+    }
+
     time = av_gettime_relative();
 
     for (int i = 0; i < opts.iters; i++) {
@@ -316,13 +324,11 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     if (!ssim_ref && sws_isSupportedInput(src->format) && 
sws_isSupportedOutput(dst->format)) {
         /* Compare against the legacy swscale API as a reference */
-        time_ref = av_gettime_relative();
-        if (scale_legacy(dst, src, mode, opts) < 0) {
+        if (scale_legacy(dst, src, mode, opts, &time_ref) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed ref %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
             goto error;
         }
-        time_ref = av_gettime_relative() - time_ref;
 
         if (sws_scale_frame(sws[2], out, dst) < 0)
             goto error;
-- 
2.52.0


>From ddd3bb8738e33b8fbeda85a505a1d3d2e6ce4af8 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Sun, 22 Feb 2026 19:47:58 +0100
Subject: [PATCH 02/31] tests/swscale: unref buffers before each iteration

Otherwise, we always pass frames that already have buffers allocated, which
breaks the no-op refcopy optimizations.

Testing with -p 0.1 -threads 16 -bench 10, on an AMD Ryzen 9 9950X3D:

 Before:
  Overall speedup=2.776x faster, min=0.133x max=629.496x
  yuv444p 1920x1080 -> yuv444p 1920x1080, flags=0x100000 dither=1
     time=9 us, ref=9 us, speedup=1.043x faster

 After:
  Overall speedup=2.721x faster, min=0.140x max=574.034x
  yuv444p 1920x1080 -> yuv444p 1920x1080, flags=0x100000 dither=1
    time=0 us, ref=28 us, speedup=516.504x faster

(The slowdown in the legacy swscale case is from swscale's lack of a no-op
refcopy optimizaton, plus the fact that it's now actually doing memory
work instead of a no-op / redundant memset)

Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/tests/swscale.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 63f566ba18..8001f0d105 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -192,6 +192,18 @@ static float get_loss(const float ssim[4])
     return 1.0 - sum;
 }
 
+static void unref_buffers(AVFrame *frame)
+{
+    for (int i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) {
+        if (!frame->buf[i])
+            break;
+        av_buffer_unref(&frame->buf[i]);
+    }
+
+    memset(frame->data, 0, sizeof(frame->data));
+    memset(frame->linesize, 0, sizeof(frame->linesize));
+}
+
 static int scale_legacy(AVFrame *dst, const AVFrame *src, struct mode mode,
                         struct options opts, int64_t *out_time)
 {
@@ -216,8 +228,10 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src, 
struct mode mode,
         goto error;
 
     int64_t time = av_gettime_relative();
-    for (int i = 0; ret >= 0 && i < opts.iters; i++)
+    for (int i = 0; ret >= 0 && i < opts.iters; i++) {
+        unref_buffers(dst);
         ret = sws_scale_frame(sws_legacy, dst, src);
+    }
     *out_time = av_gettime_relative() - time;
 
 error:
@@ -284,6 +298,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     time = av_gettime_relative();
 
     for (int i = 0; i < opts.iters; i++) {
+        unref_buffers(dst);
         if (sws_scale_frame(sws[1], dst, src) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
-- 
2.52.0


>From 4c6191f23c6c6e436c374503a076f712dd772355 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 22:37:37 +0100
Subject: [PATCH 03/31] swscale/tests/swscale: always allocate frame in
 scale_legacy()

Legacy swscale may overwrite the pixel formats in the context (see
handle_formats() in libswscale/utils.c). This may lead to an issue
where, when sws_frame_start() allocates a new frame, it uses the wrong
pixel format.

Instead of fixing the issue in swscale, just make sure dst is always
allocated prior to calling the legacy scaler.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 8001f0d105..d85dcda726 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -224,14 +224,20 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src, 
struct mode mode,
     sws_legacy->dither     = mode.dither;
     sws_legacy->threads    = opts.threads;
 
+    av_frame_unref(dst);
+    dst->width  = sws_legacy->dst_w;
+    dst->height = sws_legacy->dst_h;
+    dst->format = sws_legacy->dst_format;
+    ret = av_frame_get_buffer(dst, 0);
+    if (ret < 0)
+        goto error;
+
     if ((ret = sws_init_context(sws_legacy, NULL, NULL)) < 0)
         goto error;
 
     int64_t time = av_gettime_relative();
-    for (int i = 0; ret >= 0 && i < opts.iters; i++) {
-        unref_buffers(dst);
+    for (int i = 0; ret >= 0 && i < opts.iters; i++)
         ret = sws_scale_frame(sws_legacy, dst, src);
-    }
     *out_time = av_gettime_relative() - time;
 
 error:
-- 
2.52.0


>From 2bf9d9fcead8d63e59235f425a319f9b34fd78bb Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Thu, 5 Mar 2026 21:03:27 +0100
Subject: [PATCH 04/31] swscale/tests/swscale: remove hardcoded dimension
 checks

Remove dimension checks originally added to please static analysis
tools. There is little reason to have arbitrary limits in this
developer test tool. The reference files are under control by the user.

This reverts f70a651b3f7 and c0f0bec2f20.
---
 libswscale/tests/swscale.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index d85dcda726..1054aa2479 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -500,7 +500,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
struct options opts)
         src_fmt = av_get_pix_fmt(src_fmt_str);
         dst_fmt = av_get_pix_fmt(dst_fmt_str);
         if (src_fmt == AV_PIX_FMT_NONE || dst_fmt == AV_PIX_FMT_NONE ||
-            sw != ref->width || sh != ref->height || dw > 8192 || dh > 8192 ||
+            sw != ref->width || sh != ref->height ||
             mode.dither >= SWS_DITHER_NB) {
             av_log(NULL, AV_LOG_FATAL, "malformed input file\n");
             return -1;
-- 
2.52.0


>From cf8d9f81c7307a73f96b2e961edb710384e89c96 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 27 Feb 2026 22:12:31 +0100
Subject: [PATCH 05/31] swscale/tests/swscale: split parse_options() out of
 main()

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 90 +++++++++++++++++++++-----------------
 1 file changed, 49 insertions(+), 41 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 1054aa2479..4a49f5b51f 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -517,25 +517,8 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
struct options opts)
     return 0;
 }
 
-int main(int argc, char **argv)
+static int parse_options(int argc, char **argv, struct options *opts, FILE 
**fp)
 {
-    struct options opts = {
-        .src_fmt = AV_PIX_FMT_NONE,
-        .dst_fmt = AV_PIX_FMT_NONE,
-        .w       = 96,
-        .h       = 96,
-        .threads = 1,
-        .iters   = 1,
-        .prob    = 1.0,
-        .flags   = -1,
-        .dither  = -1,
-    };
-
-    AVFrame *rgb = NULL, *ref = NULL;
-    FILE *fp = NULL;
-    AVLFG rand;
-    int ret = -1;
-
     for (int i = 1; i < argc; i += 2) {
         if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
             fprintf(stderr,
@@ -566,68 +549,93 @@ int main(int argc, char **argv)
                     "   -v <level>\n"
                     "       Enable log verbosity at given level\n"
             );
-            return 0;
+            exit(0);
         }
         if (argv[i][0] != '-' || i + 1 == argc)
             goto bad_option;
         if (!strcmp(argv[i], "-ref")) {
-            fp = fopen(argv[i + 1], "r");
-            if (!fp) {
+            *fp = fopen(argv[i + 1], "r");
+            if (!*fp) {
                 fprintf(stderr, "could not open '%s'\n", argv[i + 1]);
-                goto error;
+                return -1;
             }
         } else if (!strcmp(argv[i], "-cpuflags")) {
             unsigned flags = av_get_cpu_flags();
             int res = av_parse_cpu_caps(&flags, argv[i + 1]);
             if (res < 0) {
                 fprintf(stderr, "invalid cpu flags %s\n", argv[i + 1]);
-                goto error;
+                return -1;
             }
             av_force_cpu_flags(flags);
         } else if (!strcmp(argv[i], "-src")) {
-            opts.src_fmt = av_get_pix_fmt(argv[i + 1]);
-            if (opts.src_fmt == AV_PIX_FMT_NONE) {
+            opts->src_fmt = av_get_pix_fmt(argv[i + 1]);
+            if (opts->src_fmt == AV_PIX_FMT_NONE) {
                 fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]);
-                goto error;
+                return -1;
             }
         } else if (!strcmp(argv[i], "-dst")) {
-            opts.dst_fmt = av_get_pix_fmt(argv[i + 1]);
-            if (opts.dst_fmt == AV_PIX_FMT_NONE) {
+            opts->dst_fmt = av_get_pix_fmt(argv[i + 1]);
+            if (opts->dst_fmt == AV_PIX_FMT_NONE) {
                 fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]);
-                goto error;
+                return -1;
             }
         } else if (!strcmp(argv[i], "-bench")) {
-            opts.bench = 1;
-            opts.iters = atoi(argv[i + 1]);
-            opts.iters = FFMAX(opts.iters, 1);
-            opts.w = 1920;
-            opts.h = 1080;
+            opts->bench = 1;
+            opts->iters = atoi(argv[i + 1]);
+            opts->iters = FFMAX(opts->iters, 1);
+            opts->w = 1920;
+            opts->h = 1080;
         } else if (!strcmp(argv[i], "-flags")) {
             SwsContext *dummy = sws_alloc_context();
             const AVOption *flags_opt = av_opt_find(dummy, "sws_flags", NULL, 
0, 0);
-            ret = av_opt_eval_flags(dummy, flags_opt, argv[i + 1], 
&opts.flags);
+            int ret = av_opt_eval_flags(dummy, flags_opt, argv[i + 1], 
&opts->flags);
             sws_free_context(&dummy);
             if (ret < 0) {
                 fprintf(stderr, "invalid flags %s\n", argv[i + 1]);
-                goto error;
+                return -1;
             }
         } else if (!strcmp(argv[i], "-dither")) {
-            opts.dither = atoi(argv[i + 1]);
+            opts->dither = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-unscaled")) {
-            opts.unscaled = atoi(argv[i + 1]);
+            opts->unscaled = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-threads")) {
-            opts.threads = atoi(argv[i + 1]);
+            opts->threads = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-p")) {
-            opts.prob = atof(argv[i + 1]);
+            opts->prob = atof(argv[i + 1]);
         } else if (!strcmp(argv[i], "-v")) {
             av_log_set_level(atoi(argv[i + 1]));
         } else {
 bad_option:
             fprintf(stderr, "bad option or argument missing (%s) see -help\n", 
argv[i]);
-            goto error;
+            return -1;
         }
     }
 
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    struct options opts = {
+        .src_fmt = AV_PIX_FMT_NONE,
+        .dst_fmt = AV_PIX_FMT_NONE,
+        .w       = 96,
+        .h       = 96,
+        .threads = 1,
+        .iters   = 1,
+        .prob    = 1.0,
+        .flags   = -1,
+        .dither  = -1,
+    };
+
+    AVFrame *rgb = NULL, *ref = NULL;
+    FILE *fp = NULL;
+    AVLFG rand;
+    int ret = -1;
+
+    if (parse_options(argc, argv, &opts, &fp) < 0)
+        goto error;
+
     ff_sfc64_init(&prng_state, 0, 0, 0, 12);
     av_lfg_init(&rand, 1);
     signal(SIGINT, exit_handler);
-- 
2.52.0


>From 44ad7bf4915d3c70e458fa92693eac180bd0c407 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 27 Feb 2026 22:14:25 +0100
Subject: [PATCH 06/31] swscale/tests/swscale: split init_ref() out of main()

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 57 ++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 4a49f5b51f..f17f160352 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -517,6 +517,40 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
struct options opts)
     return 0;
 }
 
+static int init_ref(AVFrame *ref, const struct options *opts)
+{
+    SwsContext *ctx = sws_alloc_context();
+    AVFrame *rgb = av_frame_alloc();
+    AVLFG rand;
+    int ret = -1;
+
+    if (!ctx || !rgb)
+        goto error;
+
+    rgb->width  = opts->w / 12;
+    rgb->height = opts->h / 12;
+    rgb->format = AV_PIX_FMT_RGBA;
+    ret = av_frame_get_buffer(rgb, 32);
+    if (ret < 0)
+        goto error;
+
+    av_lfg_init(&rand, 1);
+    for (int y = 0; y < rgb->height; y++) {
+        for (int x = 0; x < rgb->width; x++) {
+            for (int c = 0; c < 4; c++)
+                rgb->data[0][y * rgb->linesize[0] + x * 4 + c] = 
av_lfg_get(&rand);
+        }
+    }
+
+    ctx->flags = SWS_BILINEAR;
+    ret = sws_scale_frame(ctx, ref, rgb);
+
+error:
+    sws_free_context(&ctx);
+    av_frame_free(&rgb);
+    return ret;
+}
+
 static int parse_options(int argc, char **argv, struct options *opts, FILE 
**fp)
 {
     for (int i = 1; i < argc; i += 2) {
@@ -628,16 +662,14 @@ int main(int argc, char **argv)
         .dither  = -1,
     };
 
-    AVFrame *rgb = NULL, *ref = NULL;
+    AVFrame *ref = NULL;
     FILE *fp = NULL;
-    AVLFG rand;
     int ret = -1;
 
     if (parse_options(argc, argv, &opts, &fp) < 0)
         goto error;
 
     ff_sfc64_init(&prng_state, 0, 0, 0, 12);
-    av_lfg_init(&rand, 1);
     signal(SIGINT, exit_handler);
 
     for (int i = 0; i < 3; i++) {
@@ -647,22 +679,6 @@ int main(int argc, char **argv)
         sws[i]->flags = SWS_BILINEAR;
     }
 
-    rgb = av_frame_alloc();
-    if (!rgb)
-        goto error;
-    rgb->width  = opts.w / 12;
-    rgb->height = opts.h / 12;
-    rgb->format = AV_PIX_FMT_RGBA;
-    if (av_frame_get_buffer(rgb, 32) < 0)
-        goto error;
-
-    for (int y = 0; y < rgb->height; y++) {
-        for (int x = 0; x < rgb->width; x++) {
-            for (int c = 0; c < 4; c++)
-                rgb->data[0][y * rgb->linesize[0] + x * 4 + c] = 
av_lfg_get(&rand);
-        }
-    }
-
     ref = av_frame_alloc();
     if (!ref)
         goto error;
@@ -670,7 +686,7 @@ int main(int argc, char **argv)
     ref->height = opts.h;
     ref->format = AV_PIX_FMT_YUVA444P;
 
-    if (sws_scale_frame(sws[0], ref, rgb) < 0)
+    if (init_ref(ref, &opts) < 0)
         goto error;
 
     ret = fp ? run_file_tests(ref, fp, opts)
@@ -680,7 +696,6 @@ int main(int argc, char **argv)
 error:
     for (int i = 0; i < 3; i++)
         sws_free_context(&sws[i]);
-    av_frame_free(&rgb);
     av_frame_free(&ref);
     if (fp)
         fclose(fp);
-- 
2.52.0


>From c383b1cbbc518317bd8c72be74666cf8b645e26a Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Tue, 23 Dec 2025 21:52:02 +0100
Subject: [PATCH 07/31] swscale/tests/swscale: pass opts and mode arguments as
 const pointers

---
 libswscale/tests/swscale.c | 80 +++++++++++++++++++-------------------
 1 file changed, 41 insertions(+), 39 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index f17f160352..cfc826973c 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -204,8 +204,9 @@ static void unref_buffers(AVFrame *frame)
     memset(frame->linesize, 0, sizeof(frame->linesize));
 }
 
-static int scale_legacy(AVFrame *dst, const AVFrame *src, struct mode mode,
-                        struct options opts, int64_t *out_time)
+static int scale_legacy(AVFrame *dst, const AVFrame *src,
+                        const struct mode *mode, const struct options *opts,
+                        int64_t *out_time)
 {
     SwsContext *sws_legacy;
     int ret;
@@ -220,9 +221,9 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src, 
struct mode mode,
     sws_legacy->dst_w      = dst->width;
     sws_legacy->dst_h      = dst->height;
     sws_legacy->dst_format = dst->format;
-    sws_legacy->flags      = mode.flags;
-    sws_legacy->dither     = mode.dither;
-    sws_legacy->threads    = opts.threads;
+    sws_legacy->flags      = mode->flags;
+    sws_legacy->dither     = mode->dither;
+    sws_legacy->threads    = opts->threads;
 
     av_frame_unref(dst);
     dst->width  = sws_legacy->dst_w;
@@ -236,7 +237,7 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src, 
struct mode mode,
         goto error;
 
     int64_t time = av_gettime_relative();
-    for (int i = 0; ret >= 0 && i < opts.iters; i++)
+    for (int i = 0; ret >= 0 && i < opts->iters; i++)
         ret = sws_scale_frame(sws_legacy, dst, src);
     *out_time = av_gettime_relative() - time;
 
@@ -247,7 +248,8 @@ error:
 
 /* Runs a series of ref -> src -> dst -> out, and compares out vs ref */
 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
-                    int dst_w, int dst_h, struct mode mode, struct options 
opts,
+                    int dst_w, int dst_h,
+                    const struct mode *mode, const struct options *opts,
                     const AVFrame *ref, const float ssim_ref[4])
 {
     AVFrame *src = NULL, *dst = NULL, *out = NULL;
@@ -291,9 +293,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         goto error;
     }
 
-    sws[1]->flags  = mode.flags;
-    sws[1]->dither = mode.dither;
-    sws[1]->threads = opts.threads;
+    sws[1]->flags  = mode->flags;
+    sws[1]->dither = mode->dither;
+    sws[1]->threads = opts->threads;
 
     if (sws_frame_setup(sws[1], dst, src) < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
@@ -303,7 +305,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     time = av_gettime_relative();
 
-    for (int i = 0; i < opts.iters; i++) {
+    for (int i = 0; i < opts->iters; i++) {
         unref_buffers(dst);
         if (sws_scale_frame(sws[1], dst, src) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
@@ -324,7 +326,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     av_log(NULL, AV_LOG_INFO, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
            av_get_pix_fmt_name(src->format), src->width, src->height,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
-           mode.flags, mode.dither);
+           mode->flags, mode->dither);
 
     av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
            ssim[0], ssim[1], ssim[2], ssim[3]);
@@ -336,7 +338,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         av_log(NULL, level, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
                av_get_pix_fmt_name(src->format), src->width, src->height,
                av_get_pix_fmt_name(dst->format), dst->width, dst->height,
-               mode.flags, mode.dither);
+               mode->flags, mode->dither);
         av_log(NULL, level, "  loss %g is %s by %g, expected loss %g\n",
                loss, bad ? "WORSE" : "worse", loss - expected_loss, 
expected_loss);
         if (bad)
@@ -384,7 +386,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         }
     }
 
-    if (opts.bench && time_ref) {
+    if (opts->bench && time_ref) {
         double ratio = (double) time_ref / time;
         if (FFMIN(time, time_ref) > 100 /* don't pollute stats with low 
precision */) {
             speedup_min = FFMIN(speedup_min, ratio);
@@ -395,11 +397,11 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
         if (av_log_get_level() >= AV_LOG_INFO) {
             printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
-                   time / opts.iters, time_ref / opts.iters, ratio,
+                   time / opts->iters, time_ref / opts->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
-    } else if (opts.bench) {
-        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", time / opts.iters);
+    } else if (opts->bench) {
+        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", time / opts->iters);
     }
 
     fflush(stdout);
@@ -417,10 +419,10 @@ static inline int fmt_is_subsampled(enum AVPixelFormat 
fmt)
            av_pix_fmt_desc_get(fmt)->log2_chroma_h != 0;
 }
 
-static int run_self_tests(const AVFrame *ref, struct options opts)
+static int run_self_tests(const AVFrame *ref, const struct options *opts)
 {
-    const int dst_w[] = { opts.w, opts.w - opts.w / 3, opts.w + opts.w / 3 };
-    const int dst_h[] = { opts.h, opts.h - opts.h / 3, opts.h + opts.h / 3 };
+    const int dst_w[] = { opts->w, opts->w - opts->w / 3, opts->w + opts->w / 
3 };
+    const int dst_h[] = { opts->h, opts->h - opts->h / 3, opts->h + opts->h / 
3 };
 
     enum AVPixelFormat src_fmt, dst_fmt,
                        src_fmt_min = 0,
@@ -428,18 +430,18 @@ static int run_self_tests(const AVFrame *ref, struct 
options opts)
                        src_fmt_max = AV_PIX_FMT_NB - 1,
                        dst_fmt_max = AV_PIX_FMT_NB - 1;
 
-    if (opts.src_fmt != AV_PIX_FMT_NONE)
-        src_fmt_min = src_fmt_max = opts.src_fmt;
-    if (opts.dst_fmt != AV_PIX_FMT_NONE)
-        dst_fmt_min = dst_fmt_max = opts.dst_fmt;
+    if (opts->src_fmt != AV_PIX_FMT_NONE)
+        src_fmt_min = src_fmt_max = opts->src_fmt;
+    if (opts->dst_fmt != AV_PIX_FMT_NONE)
+        dst_fmt_min = dst_fmt_max = opts->dst_fmt;
 
     for (src_fmt = src_fmt_min; src_fmt <= src_fmt_max; src_fmt++) {
-        if (opts.unscaled && fmt_is_subsampled(src_fmt))
+        if (opts->unscaled && fmt_is_subsampled(src_fmt))
             continue;
         if (!sws_test_format(src_fmt, 0) || !sws_test_format(src_fmt, 1))
             continue;
         for (dst_fmt = dst_fmt_min; dst_fmt <= dst_fmt_max; dst_fmt++) {
-            if (opts.unscaled && fmt_is_subsampled(dst_fmt))
+            if (opts->unscaled && fmt_is_subsampled(dst_fmt))
                 continue;
             if (!sws_test_format(dst_fmt, 0) || !sws_test_format(dst_fmt, 1))
                 continue;
@@ -447,24 +449,24 @@ static int run_self_tests(const AVFrame *ref, struct 
options opts)
                 for (int w = 0; w < FF_ARRAY_ELEMS(dst_w); w++) {
                     for (int f = 0; f < FF_ARRAY_ELEMS(flags); f++) {
                         struct mode mode = {
-                            .flags  = opts.flags  >= 0 ? opts.flags  : 
flags[f],
-                            .dither = opts.dither >= 0 ? opts.dither : 
SWS_DITHER_AUTO,
+                            .flags  = opts->flags  >= 0 ? opts->flags  : 
flags[f],
+                            .dither = opts->dither >= 0 ? opts->dither : 
SWS_DITHER_AUTO,
                         };
 
-                        if (ff_sfc64_get(&prng_state) > UINT64_MAX * opts.prob)
+                        if (ff_sfc64_get(&prng_state) > UINT64_MAX * 
opts->prob)
                             continue;
 
                         if (run_test(src_fmt, dst_fmt, dst_w[w], dst_h[h],
-                                     mode, opts, ref, NULL) < 0)
+                                     &mode, opts, ref, NULL) < 0)
                             return -1;
 
-                        if (opts.flags >= 0 || opts.unscaled)
+                        if (opts->flags >= 0 || opts->unscaled)
                             break;
                     }
-                    if (opts.unscaled)
+                    if (opts->unscaled)
                         break;
                 }
-                if (opts.unscaled)
+                if (opts->unscaled)
                     break;
             }
         }
@@ -473,7 +475,7 @@ static int run_self_tests(const AVFrame *ref, struct 
options opts)
     return 0;
 }
 
-static int run_file_tests(const AVFrame *ref, FILE *fp, struct options opts)
+static int run_file_tests(const AVFrame *ref, FILE *fp, const struct options 
*opts)
 {
     char buf[256];
     int ret;
@@ -506,11 +508,11 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
struct options opts)
             return -1;
         }
 
-        if (opts.src_fmt != AV_PIX_FMT_NONE && src_fmt != opts.src_fmt ||
-            opts.dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts.dst_fmt)
+        if (opts->src_fmt != AV_PIX_FMT_NONE && src_fmt != opts->src_fmt ||
+            opts->dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts->dst_fmt)
             continue;
 
-        if (run_test(src_fmt, dst_fmt, dw, dh, mode, opts, ref, ssim) < 0)
+        if (run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, ssim) < 0)
             return -1;
     }
 
@@ -689,8 +691,8 @@ int main(int argc, char **argv)
     if (init_ref(ref, &opts) < 0)
         goto error;
 
-    ret = fp ? run_file_tests(ref, fp, opts)
-             : run_self_tests(ref, opts);
+    ret = fp ? run_file_tests(ref, fp, &opts)
+             : run_self_tests(ref, &opts);
 
     /* fall through */
 error:
-- 
2.52.0


>From d1f273ee1500e36eb90555ac940cd4a6fdb2f891 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Tue, 23 Dec 2025 21:03:35 +0100
Subject: [PATCH 08/31] swscale/tests/swscale: give names to SwsContext
 variables

---
 libswscale/tests/swscale.c | 41 ++++++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index cfc826973c..eb5d1183e4 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -70,7 +70,11 @@ const SwsFlags flags[] = {
 };
 
 static FFSFC64 prng_state;
-static SwsContext *sws[3]; /* reused between tests for efficiency */
+
+/* reused between tests for efficiency */
+static SwsContext *sws_ref_src;
+static SwsContext *sws_src_dst;
+static SwsContext *sws_dst_out;
 
 static double speedup_logavg;
 static double speedup_min = 1e10;
@@ -287,17 +291,17 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     dst->width  = dst_w;
     dst->height = dst_h;
 
-    if (sws_scale_frame(sws[0], src, ref) < 0) {
+    if (sws_scale_frame(sws_ref_src, src, ref) < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                av_get_pix_fmt_name(ref->format), 
av_get_pix_fmt_name(src->format));
         goto error;
     }
 
-    sws[1]->flags  = mode->flags;
-    sws[1]->dither = mode->dither;
-    sws[1]->threads = opts->threads;
+    sws_src_dst->flags  = mode->flags;
+    sws_src_dst->dither = mode->dither;
+    sws_src_dst->threads = opts->threads;
 
-    if (sws_frame_setup(sws[1], dst, src) < 0) {
+    if (sws_frame_setup(sws_src_dst, dst, src) < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
                av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
         goto error;
@@ -307,7 +311,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     for (int i = 0; i < opts->iters; i++) {
         unref_buffers(dst);
-        if (sws_scale_frame(sws[1], dst, src) < 0) {
+        if (sws_scale_frame(sws_src_dst, dst, src) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
             goto error;
@@ -316,7 +320,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     time = av_gettime_relative() - time;
 
-    if (sws_scale_frame(sws[2], out, dst) < 0) {
+    if (sws_scale_frame(sws_dst_out, out, dst) < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                av_get_pix_fmt_name(dst->format), 
av_get_pix_fmt_name(out->format));
         goto error;
@@ -353,7 +357,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
             goto error;
         }
 
-        if (sws_scale_frame(sws[2], out, dst) < 0)
+        if (sws_scale_frame(sws_dst_out, out, dst) < 0)
             goto error;
 
         get_ssim(ssim_sws, out, ref, comps);
@@ -674,12 +678,14 @@ int main(int argc, char **argv)
     ff_sfc64_init(&prng_state, 0, 0, 0, 12);
     signal(SIGINT, exit_handler);
 
-    for (int i = 0; i < 3; i++) {
-        sws[i] = sws_alloc_context();
-        if (!sws[i])
-            goto error;
-        sws[i]->flags = SWS_BILINEAR;
-    }
+    sws_ref_src = sws_alloc_context();
+    sws_src_dst = sws_alloc_context();
+    sws_dst_out = sws_alloc_context();
+    if (!sws_ref_src || !sws_src_dst || !sws_dst_out)
+        goto error;
+    sws_ref_src->flags = SWS_BILINEAR;
+    sws_src_dst->flags = SWS_BILINEAR;
+    sws_dst_out->flags = SWS_BILINEAR;
 
     ref = av_frame_alloc();
     if (!ref)
@@ -696,8 +702,9 @@ int main(int argc, char **argv)
 
     /* fall through */
 error:
-    for (int i = 0; i < 3; i++)
-        sws_free_context(&sws[i]);
+    sws_free_context(&sws_ref_src);
+    sws_free_context(&sws_src_dst);
+    sws_free_context(&sws_dst_out);
     av_frame_free(&ref);
     if (fp)
         fclose(fp);
-- 
2.52.0


>From b95bed54817d0d04a0e4931b4157748d38caa022 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Mon, 2 Mar 2026 16:21:29 +0100
Subject: [PATCH 09/31] swscale/tests/swscale: make auxiliary conversions
 bitexact and accurate_rnd

This prevents the propagation of dither_error across frames, and should
also improve reproducibility across platforms.

Also remove setting of flags for sws_src_dst early on, since it will
inevitably be overwritten during the tests.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index eb5d1183e4..e9704cf6fb 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -548,7 +548,7 @@ static int init_ref(AVFrame *ref, const struct options 
*opts)
         }
     }
 
-    ctx->flags = SWS_BILINEAR;
+    ctx->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
     ret = sws_scale_frame(ctx, ref, rgb);
 
 error:
@@ -683,9 +683,8 @@ int main(int argc, char **argv)
     sws_dst_out = sws_alloc_context();
     if (!sws_ref_src || !sws_src_dst || !sws_dst_out)
         goto error;
-    sws_ref_src->flags = SWS_BILINEAR;
-    sws_src_dst->flags = SWS_BILINEAR;
-    sws_dst_out->flags = SWS_BILINEAR;
+    sws_ref_src->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
+    sws_dst_out->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
 
     ref = av_frame_alloc();
     if (!ref)
-- 
2.52.0


>From 190da0e70fbba3ca9e47b1a9d573de0a129e66ee Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Tue, 3 Mar 2026 11:25:14 +0100
Subject: [PATCH 10/31] swscale/tests/swscale: avoid redundant ref->src
 conversions

The ref->src conversion only needs to be performed once per source
pixel format.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 72 +++++++++++++++++++++++++-------------
 1 file changed, 48 insertions(+), 24 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index e9704cf6fb..5306e23e6a 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -254,9 +254,9 @@ error:
 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
                     int dst_w, int dst_h,
                     const struct mode *mode, const struct options *opts,
-                    const AVFrame *ref, const float ssim_ref[4])
+                    const AVFrame *ref, AVFrame *src, const float ssim_ref[4])
 {
-    AVFrame *src = NULL, *dst = NULL, *out = NULL;
+    AVFrame *dst = NULL, *out = NULL;
     float ssim[4], ssim_sws[4];
     const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
     int64_t time, time_ref = 0;
@@ -274,29 +274,33 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     const float expected_loss = get_loss(ssim_expected);
     float loss;
 
-    src = av_frame_alloc();
     dst = av_frame_alloc();
     out = av_frame_alloc();
-    if (!src || !dst || !out)
+    if (!dst || !out)
         goto error;
 
-    av_frame_copy_props(src, ref);
+    if (src->format != src_fmt) {
+        av_frame_unref(src);
+        av_frame_copy_props(src, ref);
+        src->width  = ref->width;
+        src->height = ref->height;
+        src->format = src_fmt;
+        if (sws_scale_frame(sws_ref_src, src, ref) < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
+                   av_get_pix_fmt_name(ref->format), 
av_get_pix_fmt_name(src->format));
+            goto error;
+        }
+    }
+
     av_frame_copy_props(dst, ref);
     av_frame_copy_props(out, ref);
-    src->width  = out->width  = ref->width;
-    src->height = out->height = ref->height;
+    out->width  = ref->width;
+    out->height = ref->height;
     out->format = ref->format;
-    src->format = src_fmt;
     dst->format = dst_fmt;
     dst->width  = dst_w;
     dst->height = dst_h;
 
-    if (sws_scale_frame(sws_ref_src, src, ref) < 0) {
-        av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
-               av_get_pix_fmt_name(ref->format), 
av_get_pix_fmt_name(src->format));
-        goto error;
-    }
-
     sws_src_dst->flags  = mode->flags;
     sws_src_dst->dither = mode->dither;
     sws_src_dst->threads = opts->threads;
@@ -411,7 +415,6 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     fflush(stdout);
     ret = 0; /* fall through */
  error:
-    av_frame_free(&src);
     av_frame_free(&dst);
     av_frame_free(&out);
     return ret;
@@ -434,6 +437,12 @@ static int run_self_tests(const AVFrame *ref, const struct 
options *opts)
                        src_fmt_max = AV_PIX_FMT_NB - 1,
                        dst_fmt_max = AV_PIX_FMT_NB - 1;
 
+    AVFrame *src = av_frame_alloc();
+    if (!src)
+        return AVERROR(ENOMEM);
+
+    int ret = 0;
+
     if (opts->src_fmt != AV_PIX_FMT_NONE)
         src_fmt_min = src_fmt_max = opts->src_fmt;
     if (opts->dst_fmt != AV_PIX_FMT_NONE)
@@ -460,9 +469,10 @@ static int run_self_tests(const AVFrame *ref, const struct 
options *opts)
                         if (ff_sfc64_get(&prng_state) > UINT64_MAX * 
opts->prob)
                             continue;
 
-                        if (run_test(src_fmt, dst_fmt, dst_w[w], dst_h[h],
-                                     &mode, opts, ref, NULL) < 0)
-                            return -1;
+                        ret = run_test(src_fmt, dst_fmt, dst_w[w], dst_h[h],
+                                       &mode, opts, ref, src, NULL);
+                        if (ret < 0)
+                            goto error;
 
                         if (opts->flags >= 0 || opts->unscaled)
                             break;
@@ -476,13 +486,21 @@ static int run_self_tests(const AVFrame *ref, const 
struct options *opts)
         }
     }
 
-    return 0;
+    ret = 0;
+
+error:
+    av_frame_free(&src);
+    return ret;
 }
 
 static int run_file_tests(const AVFrame *ref, FILE *fp, const struct options 
*opts)
 {
     char buf[256];
-    int ret;
+    int ret = 0;
+
+    AVFrame *src = av_frame_alloc();
+    if (!src)
+        return AVERROR(ENOMEM);
 
     while (fgets(buf, sizeof(buf), fp)) {
         char src_fmt_str[21], dst_fmt_str[21];
@@ -509,18 +527,24 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
             sw != ref->width || sh != ref->height ||
             mode.dither >= SWS_DITHER_NB) {
             av_log(NULL, AV_LOG_FATAL, "malformed input file\n");
-            return -1;
+            ret = -1;
+            goto error;
         }
 
         if (opts->src_fmt != AV_PIX_FMT_NONE && src_fmt != opts->src_fmt ||
             opts->dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts->dst_fmt)
             continue;
 
-        if (run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, ssim) < 0)
-            return -1;
+        ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, ssim);
+        if (ret < 0)
+            goto error;
     }
 
-    return 0;
+    ret = 0;
+
+error:
+    av_frame_free(&src);
+    return ret;
 }
 
 static int init_ref(AVFrame *ref, const struct options *opts)
-- 
2.52.0


>From 47856a9f7810b20ec7109c5c35a84d073db9b23d Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 16:32:29 +0100
Subject: [PATCH 11/31] swscale/tests/swscale: add -s option to set frame size

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 5306e23e6a..d648522801 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -28,6 +28,7 @@
 
 #undef HAVE_AV_CONFIG_H
 #include "libavutil/cpu.h"
+#include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/lfg.h"
 #include "libavutil/sfc64.h"
@@ -598,8 +599,10 @@ static int parse_options(int argc, char **argv, struct 
options *opts, FILE **fp)
                     "       Only test the specified destination pixel format\n"
                     "   -src <pixfmt>\n"
                     "       Only test the specified source pixel format\n"
+                    "   -s <size>\n"
+                    "       Set frame size (WxH or abbreviation)\n"
                     "   -bench <iters>\n"
-                    "       Run benchmarks with the specified number of 
iterations. This mode also increases the size of the test images\n"
+                    "       Run benchmarks with the specified number of 
iterations. This mode also sets the frame size to 1920x1080 (unless -s is 
specified)\n"
                     "   -flags <flags>\n"
                     "       Test with a specific combination of flags\n"
                     "   -dither <mode>\n"
@@ -643,12 +646,15 @@ static int parse_options(int argc, char **argv, struct 
options *opts, FILE **fp)
                 fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]);
                 return -1;
             }
+        } else if (!strcmp(argv[i], "-s")) {
+            if (av_parse_video_size(&opts->w, &opts->h, argv[i + 1]) < 0) {
+                fprintf(stderr, "invalid frame size %s\n", argv[i + 1]);
+                return -1;
+            }
         } else if (!strcmp(argv[i], "-bench")) {
             opts->bench = 1;
             opts->iters = atoi(argv[i + 1]);
             opts->iters = FFMAX(opts->iters, 1);
-            opts->w = 1920;
-            opts->h = 1080;
         } else if (!strcmp(argv[i], "-flags")) {
             SwsContext *dummy = sws_alloc_context();
             const AVOption *flags_opt = av_opt_find(dummy, "sws_flags", NULL, 
0, 0);
@@ -675,6 +681,11 @@ bad_option:
         }
     }
 
+    if (opts->w < 0 || opts->h < 0) {
+        opts->w = opts->bench ? 1920 : 96;
+        opts->h = opts->bench ? 1080 : 96;
+    }
+
     return 0;
 }
 
@@ -683,8 +694,8 @@ int main(int argc, char **argv)
     struct options opts = {
         .src_fmt = AV_PIX_FMT_NONE,
         .dst_fmt = AV_PIX_FMT_NONE,
-        .w       = 96,
-        .h       = 96,
+        .w       = -1,
+        .h       = -1,
         .threads = 1,
         .iters   = 1,
         .prob    = 1.0,
-- 
2.52.0


>From 42f0011e0bb2f5badd8ffef33a1beace3234ff6b Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 22:04:49 +0100
Subject: [PATCH 12/31] swscale/unscaled: fix planarCopyWrapper for float
 formats with same endianness

---
 libswscale/swscale_unscaled.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libswscale/swscale_unscaled.c b/libswscale/swscale_unscaled.c
index f612f88c4d..c80731cee4 100644
--- a/libswscale/swscale_unscaled.c
+++ b/libswscale/swscale_unscaled.c
@@ -2355,6 +2355,8 @@ static int planarCopyWrapper(SwsInternal *c, const 
uint8_t *const src[],
             } else {
                 if (is16BPS(c->opts.src_format) && is16BPS(c->opts.dst_format))
                     length *= 2;
+                else if (isFloat(c->opts.src_format) && 
isFloat(c->opts.dst_format))
+                    length *= 4;
                 else if (desc_src->comp[0].depth == 1)
                     length >>= 3; // monowhite/black
                 for (i = 0; i < height; i++) {
-- 
2.52.0


>From ee8f70ab115d9ac88976ecd39739b13df520a50b Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 27 Feb 2026 22:07:59 +0100
Subject: [PATCH 13/31] Revert "tests/swscale: check supported inputs for
 legacy swscale separately"

Support for input and output formats are already checked in run_self_tests().

This reverts commit a22faeb992b2ac1d4b0e5222c02a65ac77aeb15b.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index d648522801..f90c5001c2 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -354,7 +354,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
             goto error;
     }
 
-    if (!ssim_ref && sws_isSupportedInput(src->format) && 
sws_isSupportedOutput(dst->format)) {
+    if (!ssim_ref) {
         /* Compare against the legacy swscale API as a reference */
         if (scale_legacy(dst, src, mode, opts, &time_ref) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed ref %s ---> %s\n",
-- 
2.52.0


>From 608eb8e015ec43cb1dc6aa4ea197103e81c8ad0d Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Tue, 3 Mar 2026 12:16:48 +0100
Subject: [PATCH 14/31] swscale/tests/swscale: split print_results() out of
 run_test()

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 123 ++++++++++++++++++++++---------------
 1 file changed, 73 insertions(+), 50 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index f90c5001c2..75304f4f4b 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -251,6 +251,64 @@ error:
     return ret;
 }
 
+static void print_results(const AVFrame *ref, AVFrame *src, AVFrame *dst,
+                          int dst_w, int dst_h,
+                          const struct mode *mode, const struct options *opts,
+                          const float ssim_ref[4], const float ssim[4],
+                          float loss_ref, float expected_loss, float loss,
+                          int64_t time_ref, int64_t time)
+{
+    av_log(NULL, AV_LOG_INFO, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
+           av_get_pix_fmt_name(src->format), src->width, src->height,
+           av_get_pix_fmt_name(dst->format), dst->width, dst->height,
+           mode->flags, mode->dither);
+
+    av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
+           ssim[0], ssim[1], ssim[2], ssim[3]);
+
+    if (loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
+        const int bad = loss - expected_loss > 1e-2;
+        const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
+        av_log(NULL, level, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
+               av_get_pix_fmt_name(src->format), src->width, src->height,
+               av_get_pix_fmt_name(dst->format), dst->width, dst->height,
+               mode->flags, mode->dither);
+        av_log(NULL, level, "  loss %g is %s by %g, expected loss %g\n",
+               loss, bad ? "WORSE" : "worse", loss - expected_loss, 
expected_loss);
+    }
+
+    if (ssim_ref) {
+        if (loss - loss_ref > 1e-4) {
+            int bad = loss - loss_ref > 1e-2;
+            av_log(NULL, bad ? AV_LOG_ERROR : AV_LOG_WARNING,
+                   "  loss %g is %s by %g, ref loss %g, "
+                   "SSIM {Y=%f U=%f V=%f A=%f}\n",
+                   loss, bad ? "WORSE" : "worse", loss - loss_ref, loss_ref,
+                   ssim_ref[0], ssim_ref[1], ssim_ref[2], ssim_ref[3]);
+        }
+    }
+
+    if (opts->bench && time_ref) {
+        double ratio = (double) time_ref / time;
+        if (FFMIN(time, time_ref) > 100 /* don't pollute stats with low 
precision */) {
+            speedup_min = FFMIN(speedup_min, ratio);
+            speedup_max = FFMAX(speedup_max, ratio);
+            speedup_logavg += log(ratio);
+            speedup_count++;
+        }
+
+        if (av_log_get_level() >= AV_LOG_INFO) {
+            printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
+                   time / opts->iters, time_ref / opts->iters, ratio,
+                   speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
+        }
+    } else if (opts->bench) {
+        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", time / opts->iters);
+    }
+
+    fflush(stdout);
+}
+
 /* Runs a series of ref -> src -> dst -> out, and compares out vs ref */
 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
                     int dst_w, int dst_h,
@@ -273,6 +331,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     const float ssim_luma = (2 * ref_var + c1) / (2 * ref_var + total_var + 
c1);
     const float ssim_expected[4] = { ssim_luma, 1, 1, 1 }; /* for simplicity */
     const float expected_loss = get_loss(ssim_expected);
+    float loss_ref = 0.;
     float loss;
 
     dst = av_frame_alloc();
@@ -332,27 +391,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     }
 
     get_ssim(ssim, out, ref, comps);
-    av_log(NULL, AV_LOG_INFO, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
-           av_get_pix_fmt_name(src->format), src->width, src->height,
-           av_get_pix_fmt_name(dst->format), dst->width, dst->height,
-           mode->flags, mode->dither);
-
-    av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
-           ssim[0], ssim[1], ssim[2], ssim[3]);
-
     loss = get_loss(ssim);
-    if (loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
-        const int bad = loss - expected_loss > 1e-2;
-        const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
-        av_log(NULL, level, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
-               av_get_pix_fmt_name(src->format), src->width, src->height,
-               av_get_pix_fmt_name(dst->format), dst->width, dst->height,
-               mode->flags, mode->dither);
-        av_log(NULL, level, "  loss %g is %s by %g, expected loss %g\n",
-               loss, bad ? "WORSE" : "worse", loss - expected_loss, 
expected_loss);
-        if (bad)
-            goto error;
-    }
+    if (loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height)
+        goto early_exit;
 
     if (!ssim_ref) {
         /* Compare against the legacy swscale API as a reference */
@@ -382,39 +423,21 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     }
 
     if (ssim_ref) {
-        const float loss_ref = get_loss(ssim_ref);
-        if (loss - loss_ref > 1e-4) {
-            int bad = loss - loss_ref > 1e-2;
-            av_log(NULL, bad ? AV_LOG_ERROR : AV_LOG_WARNING,
-                   "  loss %g is %s by %g, ref loss %g, "
-                   "SSIM {Y=%f U=%f V=%f A=%f}\n",
-                   loss, bad ? "WORSE" : "worse", loss - loss_ref, loss_ref,
-                   ssim_ref[0], ssim_ref[1], ssim_ref[2], ssim_ref[3]);
-            if (bad)
-                goto error;
-        }
+        loss_ref = get_loss(ssim_ref);
+        if (loss - loss_ref > 1e-2)
+            goto early_exit;
     }
 
-    if (opts->bench && time_ref) {
-        double ratio = (double) time_ref / time;
-        if (FFMIN(time, time_ref) > 100 /* don't pollute stats with low 
precision */) {
-            speedup_min = FFMIN(speedup_min, ratio);
-            speedup_max = FFMAX(speedup_max, ratio);
-            speedup_logavg += log(ratio);
-            speedup_count++;
-        }
-
-        if (av_log_get_level() >= AV_LOG_INFO) {
-            printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
-                   time / opts->iters, time_ref / opts->iters, ratio,
-                   speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
-        }
-    } else if (opts->bench) {
-        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", time / opts->iters);
-    }
-
-    fflush(stdout);
     ret = 0; /* fall through */
+
+early_exit:
+    print_results(ref, src, dst,
+                  dst_w, dst_h,
+                  mode, opts,
+                  ssim_ref, ssim,
+                  loss_ref, expected_loss, loss,
+                  time_ref, time);
+
  error:
     av_frame_free(&dst);
     av_frame_free(&out);
-- 
2.52.0


>From e94c9c851b0ad01a490a71b78e1a610b98e307c8 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 17:05:18 +0100
Subject: [PATCH 15/31] swscale/tests/swscale: introduce struct test_results to
 collect results

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 85 ++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 41 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 75304f4f4b..ac7a78a7d8 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -59,6 +59,12 @@ struct mode {
     SwsDither dither;
 };
 
+struct test_results {
+    float ssim[4];
+    float loss;
+    int64_t time;
+};
+
 const SwsFlags flags[] = {
     0, // test defaults
     SWS_FAST_BILINEAR,
@@ -254,9 +260,9 @@ error:
 static void print_results(const AVFrame *ref, AVFrame *src, AVFrame *dst,
                           int dst_w, int dst_h,
                           const struct mode *mode, const struct options *opts,
-                          const float ssim_ref[4], const float ssim[4],
-                          float loss_ref, float expected_loss, float loss,
-                          int64_t time_ref, int64_t time)
+                          const struct test_results *r,
+                          const struct test_results *ref_r,
+                          float expected_loss)
 {
     av_log(NULL, AV_LOG_INFO, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
            av_get_pix_fmt_name(src->format), src->width, src->height,
@@ -264,33 +270,33 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
            mode->flags, mode->dither);
 
     av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
-           ssim[0], ssim[1], ssim[2], ssim[3]);
+           r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3]);
 
-    if (loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
-        const int bad = loss - expected_loss > 1e-2;
+    if (r->loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
+        const int bad = r->loss - expected_loss > 1e-2;
         const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
         av_log(NULL, level, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
                av_get_pix_fmt_name(src->format), src->width, src->height,
                av_get_pix_fmt_name(dst->format), dst->width, dst->height,
                mode->flags, mode->dither);
         av_log(NULL, level, "  loss %g is %s by %g, expected loss %g\n",
-               loss, bad ? "WORSE" : "worse", loss - expected_loss, 
expected_loss);
+               r->loss, bad ? "WORSE" : "worse", r->loss - expected_loss, 
expected_loss);
     }
 
-    if (ssim_ref) {
-        if (loss - loss_ref > 1e-4) {
-            int bad = loss - loss_ref > 1e-2;
+    if (!isnan(ref_r->loss)) {
+        if (r->loss - ref_r->loss > 1e-4) {
+            int bad = r->loss - ref_r->loss > 1e-2;
             av_log(NULL, bad ? AV_LOG_ERROR : AV_LOG_WARNING,
                    "  loss %g is %s by %g, ref loss %g, "
                    "SSIM {Y=%f U=%f V=%f A=%f}\n",
-                   loss, bad ? "WORSE" : "worse", loss - loss_ref, loss_ref,
-                   ssim_ref[0], ssim_ref[1], ssim_ref[2], ssim_ref[3]);
+                   r->loss, bad ? "WORSE" : "worse", r->loss - ref_r->loss, 
ref_r->loss,
+                   ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], 
ref_r->ssim[3]);
         }
     }
 
-    if (opts->bench && time_ref) {
-        double ratio = (double) time_ref / time;
-        if (FFMIN(time, time_ref) > 100 /* don't pollute stats with low 
precision */) {
+    if (opts->bench && ref_r->time) {
+        double ratio = (double) ref_r->time / r->time;
+        if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with low 
precision */) {
             speedup_min = FFMIN(speedup_min, ratio);
             speedup_max = FFMAX(speedup_max, ratio);
             speedup_logavg += log(ratio);
@@ -299,11 +305,11 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
 
         if (av_log_get_level() >= AV_LOG_INFO) {
             printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
-                   time / opts->iters, time_ref / opts->iters, ratio,
+                   r->time / opts->iters, ref_r->time / opts->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
     } else if (opts->bench) {
-        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", time / opts->iters);
+        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", r->time / 
opts->iters);
     }
 
     fflush(stdout);
@@ -316,9 +322,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
                     const AVFrame *ref, AVFrame *src, const float ssim_ref[4])
 {
     AVFrame *dst = NULL, *out = NULL;
-    float ssim[4], ssim_sws[4];
     const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
-    int64_t time, time_ref = 0;
+    int64_t time;
     int ret = -1;
 
     /* Estimate the expected amount of loss from bit depth reduction */
@@ -331,8 +336,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     const float ssim_luma = (2 * ref_var + c1) / (2 * ref_var + total_var + 
c1);
     const float ssim_expected[4] = { ssim_luma, 1, 1, 1 }; /* for simplicity */
     const float expected_loss = get_loss(ssim_expected);
-    float loss_ref = 0.;
-    float loss;
+
+    struct test_results r;
+    struct test_results ref_r = { .loss = NAN };
 
     dst = av_frame_alloc();
     out = av_frame_alloc();
@@ -382,7 +388,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         }
     }
 
-    time = av_gettime_relative() - time;
+    r.time = av_gettime_relative() - time;
 
     if (sws_scale_frame(sws_dst_out, out, dst) < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
@@ -390,14 +396,14 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         goto error;
     }
 
-    get_ssim(ssim, out, ref, comps);
-    loss = get_loss(ssim);
-    if (loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height)
+    get_ssim(r.ssim, out, ref, comps);
+    r.loss = get_loss(r.ssim);
+    if (r.loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height)
         goto early_exit;
 
     if (!ssim_ref) {
         /* Compare against the legacy swscale API as a reference */
-        if (scale_legacy(dst, src, mode, opts, &time_ref) < 0) {
+        if (scale_legacy(dst, src, mode, opts, &ref_r.time) < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed ref %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
             goto error;
@@ -406,7 +412,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         if (sws_scale_frame(sws_dst_out, out, dst) < 0)
             goto error;
 
-        get_ssim(ssim_sws, out, ref, comps);
+        get_ssim(ref_r.ssim, out, ref, comps);
 
         /* Legacy swscale does not perform bit accurate upconversions of low
          * bit depth RGB. This artificially improves the SSIM score because the
@@ -416,17 +422,15 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
          * higher than it theoretically "should" be. */
         if (src_var > dst_var) {
             const float src_loss = (2 * ref_var + c1) / (2 * ref_var + src_var 
+ c1);
-            ssim_sws[0] = FFMIN(ssim_sws[0], src_loss);
+            ref_r.ssim[0] = FFMIN(ref_r.ssim[0], src_loss);
         }
-
-        ssim_ref = ssim_sws;
+    } else {
+        memcpy(ref_r.ssim, ssim_ref, sizeof(ref_r.ssim));
     }
 
-    if (ssim_ref) {
-        loss_ref = get_loss(ssim_ref);
-        if (loss - loss_ref > 1e-2)
-            goto early_exit;
-    }
+    ref_r.loss = get_loss(ref_r.ssim);
+    if (r.loss - ref_r.loss > 1e-2)
+        goto early_exit;
 
     ret = 0; /* fall through */
 
@@ -434,9 +438,8 @@ early_exit:
     print_results(ref, src, dst,
                   dst_w, dst_h,
                   mode, opts,
-                  ssim_ref, ssim,
-                  loss_ref, expected_loss, loss,
-                  time_ref, time);
+                  &r, &ref_r,
+                  expected_loss);
 
  error:
     av_frame_free(&dst);
@@ -531,7 +534,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
         enum AVPixelFormat src_fmt;
         enum AVPixelFormat dst_fmt;
         int sw, sh, dw, dh;
-        float ssim[4];
+        struct test_results r;
         struct mode mode;
 
         ret = sscanf(buf,
@@ -539,7 +542,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
                      "SSIM {Y=%f U=%f V=%f A=%f}\n",
                      src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh,
                      &mode.flags, &mode.dither,
-                     &ssim[0], &ssim[1], &ssim[2], &ssim[3]);
+                     &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3]);
         if (ret != 12) {
             printf("%s", buf);
             continue;
@@ -559,7 +562,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
             opts->dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts->dst_fmt)
             continue;
 
-        ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, ssim);
+        ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, 
r.ssim);
         if (ret < 0)
             goto error;
     }
-- 
2.52.0


>From f11688bd2ad1526ab0f99ed98817bdcba2ea4dd2 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 21:27:56 +0100
Subject: [PATCH 16/31] swscale/tests/swscale: reorder test results output

They will eventually be merged into the same output line.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 40 +++++++++++++++++++-------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index ac7a78a7d8..d995e40aed 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -272,6 +272,26 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
     av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
            r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3]);
 
+    if (opts->bench && ref_r->time) {
+        double ratio = (double) ref_r->time / r->time;
+        if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with low 
precision */) {
+            speedup_min = FFMIN(speedup_min, ratio);
+            speedup_max = FFMAX(speedup_max, ratio);
+            speedup_logavg += log(ratio);
+            speedup_count++;
+        }
+
+        if (av_log_get_level() >= AV_LOG_INFO) {
+            printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
+                   r->time / opts->iters, ref_r->time / opts->iters, ratio,
+                   speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
+        }
+    } else if (opts->bench) {
+        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", r->time / 
opts->iters);
+    }
+
+    fflush(stdout);
+
     if (r->loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
         const int bad = r->loss - expected_loss > 1e-2;
         const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
@@ -293,26 +313,6 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
                    ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], 
ref_r->ssim[3]);
         }
     }
-
-    if (opts->bench && ref_r->time) {
-        double ratio = (double) ref_r->time / r->time;
-        if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with low 
precision */) {
-            speedup_min = FFMIN(speedup_min, ratio);
-            speedup_max = FFMAX(speedup_max, ratio);
-            speedup_logavg += log(ratio);
-            speedup_count++;
-        }
-
-        if (av_log_get_level() >= AV_LOG_INFO) {
-            printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
-                   r->time / opts->iters, ref_r->time / opts->iters, ratio,
-                   speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
-        }
-    } else if (opts->bench) {
-        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", r->time / 
opts->iters);
-    }
-
-    fflush(stdout);
 }
 
 /* Runs a series of ref -> src -> dst -> out, and compares out vs ref */
-- 
2.52.0


>From 5b18f5310c99027c6654cbcc43107da55ea05333 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 19:36:32 +0100
Subject: [PATCH 17/31] swscale/tests/swscale: cosmetics (avoid assignments in
 conditions)

---
 libswscale/tests/swscale.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index d995e40aed..6bcb63a156 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -244,7 +244,8 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src,
     if (ret < 0)
         goto error;
 
-    if ((ret = sws_init_context(sws_legacy, NULL, NULL)) < 0)
+    ret = sws_init_context(sws_legacy, NULL, NULL);
+    if (ret < 0)
         goto error;
 
     int64_t time = av_gettime_relative();
-- 
2.52.0


>From 703c051cd85904d79ee2c6ecb88b89bf32851e65 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 17:20:19 +0100
Subject: [PATCH 18/31] swscale/tests/swscale: propagate error out of
 run_test()

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 6bcb63a156..84a15a8e6a 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -325,7 +325,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     AVFrame *dst = NULL, *out = NULL;
     const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
     int64_t time;
-    int ret = -1;
+    int ret;
 
     /* Estimate the expected amount of loss from bit depth reduction */
     const float c1 = 0.01 * 0.01; /* stabilization constant */
@@ -343,8 +343,10 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     dst = av_frame_alloc();
     out = av_frame_alloc();
-    if (!dst || !out)
+    if (!dst || !out) {
+        ret = AVERROR(ENOMEM);
         goto error;
+    }
 
     if (src->format != src_fmt) {
         av_frame_unref(src);
@@ -352,7 +354,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         src->width  = ref->width;
         src->height = ref->height;
         src->format = src_fmt;
-        if (sws_scale_frame(sws_ref_src, src, ref) < 0) {
+        ret = sws_scale_frame(sws_ref_src, src, ref);
+        if (ret < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                    av_get_pix_fmt_name(ref->format), 
av_get_pix_fmt_name(src->format));
             goto error;
@@ -372,7 +375,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     sws_src_dst->dither = mode->dither;
     sws_src_dst->threads = opts->threads;
 
-    if (sws_frame_setup(sws_src_dst, dst, src) < 0) {
+    ret = sws_frame_setup(sws_src_dst, dst, src);
+    if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
                av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
         goto error;
@@ -382,7 +386,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     for (int i = 0; i < opts->iters; i++) {
         unref_buffers(dst);
-        if (sws_scale_frame(sws_src_dst, dst, src) < 0) {
+        ret = sws_scale_frame(sws_src_dst, dst, src);
+        if (ret < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
             goto error;
@@ -391,7 +396,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     r.time = av_gettime_relative() - time;
 
-    if (sws_scale_frame(sws_dst_out, out, dst) < 0) {
+    ret = sws_scale_frame(sws_dst_out, out, dst);
+    if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
                av_get_pix_fmt_name(dst->format), 
av_get_pix_fmt_name(out->format));
         goto error;
@@ -399,18 +405,22 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     get_ssim(r.ssim, out, ref, comps);
     r.loss = get_loss(r.ssim);
-    if (r.loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height)
+    if (r.loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height) {
+        ret = -1;
         goto early_exit;
+    }
 
     if (!ssim_ref) {
         /* Compare against the legacy swscale API as a reference */
-        if (scale_legacy(dst, src, mode, opts, &ref_r.time) < 0) {
+        ret = scale_legacy(dst, src, mode, opts, &ref_r.time);
+        if (ret < 0) {
             av_log(NULL, AV_LOG_ERROR, "Failed ref %s ---> %s\n",
                    av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
             goto error;
         }
 
-        if (sws_scale_frame(sws_dst_out, out, dst) < 0)
+        ret = sws_scale_frame(sws_dst_out, out, dst);
+        if (ret < 0)
             goto error;
 
         get_ssim(ref_r.ssim, out, ref, comps);
@@ -430,8 +440,10 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     }
 
     ref_r.loss = get_loss(ref_r.ssim);
-    if (r.loss - ref_r.loss > 1e-2)
+    if (r.loss - ref_r.loss > 1e-2) {
+        ret = -1;
         goto early_exit;
+    }
 
     ret = 0; /* fall through */
 
-- 
2.52.0


>From cae1af912133653d9206d1f9544fc08d2ac75b6b Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 17:29:03 +0100
Subject: [PATCH 19/31] swscale/tests/swscale: split init_frame() out of
 run_test()

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 36 +++++++++++++++++++++---------------
 1 file changed, 21 insertions(+), 15 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 84a15a8e6a..d292725aec 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -316,6 +316,20 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
     }
 }
 
+static int init_frame(AVFrame **pframe, const AVFrame *ref,
+                      int width, int height, enum AVPixelFormat format)
+{
+    AVFrame *frame = av_frame_alloc();
+    if (!frame)
+        return AVERROR(ENOMEM);
+    av_frame_copy_props(frame, ref);
+    frame->width  = width;
+    frame->height = height;
+    frame->format = format;
+    *pframe = frame;
+    return 0;
+}
+
 /* Runs a series of ref -> src -> dst -> out, and compares out vs ref */
 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
                     int dst_w, int dst_h,
@@ -341,13 +355,6 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     struct test_results r;
     struct test_results ref_r = { .loss = NAN };
 
-    dst = av_frame_alloc();
-    out = av_frame_alloc();
-    if (!dst || !out) {
-        ret = AVERROR(ENOMEM);
-        goto error;
-    }
-
     if (src->format != src_fmt) {
         av_frame_unref(src);
         av_frame_copy_props(src, ref);
@@ -362,14 +369,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         }
     }
 
-    av_frame_copy_props(dst, ref);
-    av_frame_copy_props(out, ref);
-    out->width  = ref->width;
-    out->height = ref->height;
-    out->format = ref->format;
-    dst->format = dst_fmt;
-    dst->width  = dst_w;
-    dst->height = dst_h;
+    ret = init_frame(&dst, ref, dst_w, dst_h, dst_fmt);
+    if (ret < 0)
+        goto error;
 
     sws_src_dst->flags  = mode->flags;
     sws_src_dst->dither = mode->dither;
@@ -396,6 +398,10 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     r.time = av_gettime_relative() - time;
 
+    ret = init_frame(&out, ref, ref->width, ref->height, ref->format);
+    if (ret < 0)
+        goto error;
+
     ret = sws_scale_frame(sws_dst_out, out, dst);
     if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
-- 
2.52.0


>From 602e96e6f3d1f74f2124bb629981929008fccafd Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 17:00:25 +0100
Subject: [PATCH 20/31] swscale/tests/swscale: add helper function to log
 sws_scale_frame() failures

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 42 ++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index d292725aec..5326e9301d 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -215,6 +215,16 @@ static void unref_buffers(AVFrame *frame)
     memset(frame->linesize, 0, sizeof(frame->linesize));
 }
 
+static int checked_sws_scale_frame(SwsContext *c, AVFrame *dst, const AVFrame 
*src)
+{
+    int ret = sws_scale_frame(c, dst, src);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
+               av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+    }
+    return ret;
+}
+
 static int scale_legacy(AVFrame *dst, const AVFrame *src,
                         const struct mode *mode, const struct options *opts,
                         int64_t *out_time)
@@ -250,7 +260,7 @@ static int scale_legacy(AVFrame *dst, const AVFrame *src,
 
     int64_t time = av_gettime_relative();
     for (int i = 0; ret >= 0 && i < opts->iters; i++)
-        ret = sws_scale_frame(sws_legacy, dst, src);
+        ret = checked_sws_scale_frame(sws_legacy, dst, src);
     *out_time = av_gettime_relative() - time;
 
 error:
@@ -361,12 +371,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         src->width  = ref->width;
         src->height = ref->height;
         src->format = src_fmt;
-        ret = sws_scale_frame(sws_ref_src, src, ref);
-        if (ret < 0) {
-            av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
-                   av_get_pix_fmt_name(ref->format), 
av_get_pix_fmt_name(src->format));
+        ret = checked_sws_scale_frame(sws_ref_src, src, ref);
+        if (ret < 0)
             goto error;
-        }
     }
 
     ret = init_frame(&dst, ref, dst_w, dst_h, dst_fmt);
@@ -388,12 +395,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 
     for (int i = 0; i < opts->iters; i++) {
         unref_buffers(dst);
-        ret = sws_scale_frame(sws_src_dst, dst, src);
-        if (ret < 0) {
-            av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
-                   av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+        ret = checked_sws_scale_frame(sws_src_dst, dst, src);
+        if (ret < 0)
             goto error;
-        }
     }
 
     r.time = av_gettime_relative() - time;
@@ -402,12 +406,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     if (ret < 0)
         goto error;
 
-    ret = sws_scale_frame(sws_dst_out, out, dst);
-    if (ret < 0) {
-        av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
-               av_get_pix_fmt_name(dst->format), 
av_get_pix_fmt_name(out->format));
+    ret = checked_sws_scale_frame(sws_dst_out, out, dst);
+    if (ret < 0)
         goto error;
-    }
 
     get_ssim(r.ssim, out, ref, comps);
     r.loss = get_loss(r.ssim);
@@ -419,13 +420,10 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     if (!ssim_ref) {
         /* Compare against the legacy swscale API as a reference */
         ret = scale_legacy(dst, src, mode, opts, &ref_r.time);
-        if (ret < 0) {
-            av_log(NULL, AV_LOG_ERROR, "Failed ref %s ---> %s\n",
-                   av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+        if (ret < 0)
             goto error;
-        }
 
-        ret = sws_scale_frame(sws_dst_out, out, dst);
+        ret = checked_sws_scale_frame(sws_dst_out, out, dst);
         if (ret < 0)
             goto error;
 
@@ -619,7 +617,7 @@ static int init_ref(AVFrame *ref, const struct options 
*opts)
     }
 
     ctx->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
-    ret = sws_scale_frame(ctx, ref, rgb);
+    ret = checked_sws_scale_frame(ctx, ref, rgb);
 
 error:
     sws_free_context(&ctx);
-- 
2.52.0


>From eba69965da292f50a9b04c075d1168d522a9aa8a Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 17:05:20 +0100
Subject: [PATCH 21/31] swscale/tests/swscale: split scale_graph() out of
 run_test()

We will eventually be able to select between running the graph-based
scaler or the legacy scaler.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 48 +++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 21 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 5326e9301d..186a5e676c 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -225,6 +225,31 @@ static int checked_sws_scale_frame(SwsContext *c, AVFrame 
*dst, const AVFrame *s
     return ret;
 }
 
+static int scale_graph(AVFrame *dst, const AVFrame *src,
+                       const struct mode *mode, const struct options *opts,
+                       int64_t *out_time)
+{
+    sws_src_dst->flags  = mode->flags;
+    sws_src_dst->dither = mode->dither;
+    sws_src_dst->threads = opts->threads;
+
+    int ret = sws_frame_setup(sws_src_dst, dst, src);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
+               av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+        return ret;
+    }
+
+    int64_t time = av_gettime_relative();
+    for (int i = 0; ret >= 0 && i < opts->iters; i++) {
+        unref_buffers(dst);
+        ret = checked_sws_scale_frame(sws_src_dst, dst, src);
+    }
+    *out_time = av_gettime_relative() - time;
+
+    return ret;
+}
+
 static int scale_legacy(AVFrame *dst, const AVFrame *src,
                         const struct mode *mode, const struct options *opts,
                         int64_t *out_time)
@@ -348,7 +373,6 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
 {
     AVFrame *dst = NULL, *out = NULL;
     const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
-    int64_t time;
     int ret;
 
     /* Estimate the expected amount of loss from bit depth reduction */
@@ -380,27 +404,9 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     if (ret < 0)
         goto error;
 
-    sws_src_dst->flags  = mode->flags;
-    sws_src_dst->dither = mode->dither;
-    sws_src_dst->threads = opts->threads;
-
-    ret = sws_frame_setup(sws_src_dst, dst, src);
-    if (ret < 0) {
-        av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
-               av_get_pix_fmt_name(src->format), 
av_get_pix_fmt_name(dst->format));
+    ret = scale_graph(dst, src, mode, opts, &r.time);
+    if (ret < 0)
         goto error;
-    }
-
-    time = av_gettime_relative();
-
-    for (int i = 0; i < opts->iters; i++) {
-        unref_buffers(dst);
-        ret = checked_sws_scale_frame(sws_src_dst, dst, src);
-        if (ret < 0)
-            goto error;
-    }
-
-    r.time = av_gettime_relative() - time;
 
     ret = init_frame(&out, ref, ref->width, ref->height, ref->format);
     if (ret < 0)
-- 
2.52.0


>From b4aa89cb5ea775166954bff80bee470809cfefd4 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 17:35:44 +0100
Subject: [PATCH 22/31] swscale/tests/swscale: restore reference file
 functionality

The test results (along with SSIM) are printed to stdout again so that
the output can be parsed by -ref.

Benchmark results have also been added to the output.

We still need to re-run the reference tests to perform benchmarks, but
this will be simplified in the next few commits.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 186a5e676c..d392a80320 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -300,12 +300,12 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
                           const struct test_results *ref_r,
                           float expected_loss)
 {
-    av_log(NULL, AV_LOG_INFO, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
+    printf("%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u, ",
            av_get_pix_fmt_name(src->format), src->width, src->height,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
            mode->flags, mode->dither);
 
-    av_log(NULL, AV_LOG_VERBOSE - 4, "  SSIM {Y=%f U=%f V=%f A=%f}\n",
+    printf("SSIM {Y=%f U=%f V=%f A=%f}",
            r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3]);
 
     if (opts->bench && ref_r->time) {
@@ -318,13 +318,14 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
         }
 
         if (av_log_get_level() >= AV_LOG_INFO) {
-            printf("  time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m\n",
+            printf(" time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m",
                    r->time / opts->iters, ref_r->time / opts->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
     } else if (opts->bench) {
-        av_log(NULL, AV_LOG_INFO, "  time=%"PRId64" us\n", r->time / 
opts->iters);
+        printf(" time=%"PRId64" us", r->time / opts->iters);
     }
+    printf("\n");
 
     fflush(stdout);
 
@@ -423,7 +424,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         goto early_exit;
     }
 
-    if (!ssim_ref) {
+    if (!ssim_ref || opts->bench) {
         /* Compare against the legacy swscale API as a reference */
         ret = scale_legacy(dst, src, mode, opts, &ref_r.time);
         if (ret < 0)
@@ -560,6 +561,9 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
         struct test_results r;
         struct mode mode;
 
+        if (!strncmp(buf, "Overall speedup", 15))
+            continue;
+
         ret = sscanf(buf,
                      "%20s %dx%d -> %20s %dx%d, flags=0x%x dither=%u, "
                      "SSIM {Y=%f U=%f V=%f A=%f}\n",
-- 
2.52.0


>From 8c0bf1735b823b0e6b33065237fdd07af02cde10 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 17:55:16 +0100
Subject: [PATCH 23/31] swscale/tests/swscale: be more strict with reference
 file format

The format of the reference file is the output which is printed to
stdout from this tool itself.

Malformed reference files cause an error, with a more descriptive error
message. Running a subset of the reference conversions is still
supported through -src and/or -dst.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index d392a80320..f40a5f58fc 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -553,7 +553,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
     if (!src)
         return AVERROR(ENOMEM);
 
-    while (fgets(buf, sizeof(buf), fp)) {
+    for (int line = 1; fgets(buf, sizeof(buf), fp); line++) {
         char src_fmt_str[21], dst_fmt_str[21];
         enum AVPixelFormat src_fmt;
         enum AVPixelFormat dst_fmt;
@@ -571,17 +571,24 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
                      &mode.flags, &mode.dither,
                      &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3]);
         if (ret != 12) {
-            printf("%s", buf);
-            continue;
+            av_log(NULL, AV_LOG_FATAL,
+                   "Malformed reference file in line %d\n", line);
+            goto error;
         }
 
         src_fmt = av_get_pix_fmt(src_fmt_str);
         dst_fmt = av_get_pix_fmt(dst_fmt_str);
-        if (src_fmt == AV_PIX_FMT_NONE || dst_fmt == AV_PIX_FMT_NONE ||
-            sw != ref->width || sh != ref->height ||
-            mode.dither >= SWS_DITHER_NB) {
-            av_log(NULL, AV_LOG_FATAL, "malformed input file\n");
-            ret = -1;
+        if (src_fmt == AV_PIX_FMT_NONE || dst_fmt == AV_PIX_FMT_NONE) {
+            av_log(NULL, AV_LOG_FATAL,
+                   "Unknown pixel formats (%s and/or %s) in line %d\n",
+                   src_fmt_str, dst_fmt_str, line);
+            goto error;
+        }
+
+        if (sw != ref->width || sh != ref->height) {
+            av_log(NULL, AV_LOG_FATAL,
+                   "Mismatching dimensions %dx%d (ref is %dx%d) in line %d\n",
+                   sw, sh, ref->width, ref->height, line);
             goto error;
         }
 
-- 
2.52.0


>From e93d43e130e56d4e7694dd7d14e3274168926df4 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Fri, 6 Mar 2026 17:36:08 +0100
Subject: [PATCH 24/31] swscale/tests/swscale: add option to run main
 conversion with legacy scaler

The legacy scaler does not trigger an error on conversions that are
much worse than the expected loss like with the graph-based scaler.

The low bit depth workaround code is duplicated in this commit, but the
other occurrence will be removed in a few commits, so I see no reason
to factor it out.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index f40a5f58fc..37db2d2757 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -52,6 +52,7 @@ struct options {
     int flags;
     int dither;
     int unscaled;
+    int legacy;
 };
 
 struct mode {
@@ -405,7 +406,8 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     if (ret < 0)
         goto error;
 
-    ret = scale_graph(dst, src, mode, opts, &r.time);
+    ret = opts->legacy ? scale_legacy(dst, src, mode, opts, &r.time)
+                       : scale_graph(dst, src, mode, opts, &r.time);
     if (ret < 0)
         goto error;
 
@@ -418,8 +420,22 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         goto error;
 
     get_ssim(r.ssim, out, ref, comps);
+
+    if (opts->legacy) {
+        /* Legacy swscale does not perform bit accurate upconversions of low
+         * bit depth RGB. This artificially improves the SSIM score because the
+         * resulting error deletes some of the input dither noise. This gives
+         * it an unfair advantage when compared against a bit exact reference.
+         * Work around this by ensuring that the reference SSIM score is not
+         * higher than it theoretically "should" be. */
+        if (src_var > dst_var) {
+            const float src_loss = (2 * ref_var + c1) / (2 * ref_var + src_var 
+ c1);
+            r.ssim[0] = FFMIN(r.ssim[0], src_loss);
+        }
+    }
+
     r.loss = get_loss(r.ssim);
-    if (r.loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= 
ref->height) {
+    if (!opts->legacy && r.loss - expected_loss > 1e-2 && dst_w >= ref->width 
&& dst_h >= ref->height) {
         ret = -1;
         goto early_exit;
     }
@@ -669,6 +685,8 @@ static int parse_options(int argc, char **argv, struct 
options *opts, FILE **fp)
                     "       Test with a specific dither mode\n"
                     "   -unscaled <1 or 0>\n"
                     "       If 1, test only conversions that do not involve 
scaling\n"
+                    "   -legacy <1 or 0>\n"
+                    "       If 1, force using legacy swscale for the main 
conversion\n"
                     "   -threads <threads>\n"
                     "       Use the specified number of threads\n"
                     "   -cpuflags <cpuflags>\n"
@@ -728,6 +746,8 @@ static int parse_options(int argc, char **argv, struct 
options *opts, FILE **fp)
             opts->dither = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-unscaled")) {
             opts->unscaled = atoi(argv[i + 1]);
+        } else if (!strcmp(argv[i], "-legacy")) {
+            opts->legacy = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-threads")) {
             opts->threads = atoi(argv[i + 1]);
         } else if (!strcmp(argv[i], "-p")) {
-- 
2.52.0


>From 3cb67b05efad30de079f98bbae0d52dd7b877e97 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 21:52:46 +0100
Subject: [PATCH 25/31] swscale/tests/swscale: make loss printing code more
 consistent

Also changed the output format for losses to scientific notation (%e),
to emphasize the order of magnitude of the loss.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 37db2d2757..58ba0844c9 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -333,23 +333,20 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
     if (r->loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= 
ref->height) {
         const int bad = r->loss - expected_loss > 1e-2;
         const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
-        av_log(NULL, level, "%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u\n",
-               av_get_pix_fmt_name(src->format), src->width, src->height,
-               av_get_pix_fmt_name(dst->format), dst->width, dst->height,
-               mode->flags, mode->dither);
-        av_log(NULL, level, "  loss %g is %s by %g, expected loss %g\n",
-               r->loss, bad ? "WORSE" : "worse", r->loss - expected_loss, 
expected_loss);
+        const char *worse_str = bad ? "WORSE" : "worse";
+        av_log(NULL, level,
+               "  loss %e is %s by %e, expected loss %e\n",
+               r->loss, worse_str, r->loss - expected_loss, expected_loss);
     }
 
-    if (!isnan(ref_r->loss)) {
-        if (r->loss - ref_r->loss > 1e-4) {
-            int bad = r->loss - ref_r->loss > 1e-2;
-            av_log(NULL, bad ? AV_LOG_ERROR : AV_LOG_WARNING,
-                   "  loss %g is %s by %g, ref loss %g, "
-                   "SSIM {Y=%f U=%f V=%f A=%f}\n",
-                   r->loss, bad ? "WORSE" : "worse", r->loss - ref_r->loss, 
ref_r->loss,
-                   ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], 
ref_r->ssim[3]);
-        }
+    if (!isnan(ref_r->loss) && r->loss - ref_r->loss > 1e-4) {
+        const int bad = r->loss - ref_r->loss > 1e-2;
+        const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
+        const char *worse_str = bad ? "WORSE" : "worse";
+        av_log(NULL, level,
+               "  loss %e is %s by %e, ref loss %e SSIM {Y=%f U=%f V=%f 
A=%f}\n",
+               r->loss, worse_str, r->loss - ref_r->loss, ref_r->loss,
+               ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], ref_r->ssim[3]);
     }
 }
 
-- 
2.52.0


>From 7e935dd70a138a0620d0e0eb3e9b3334628cece3 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 20:06:21 +0100
Subject: [PATCH 26/31] swscale/tests/swscale: allow passing -bench 0 to
 disable benchmarks

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 58ba0844c9..3aaa69751c 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -727,9 +727,14 @@ static int parse_options(int argc, char **argv, struct 
options *opts, FILE **fp)
                 return -1;
             }
         } else if (!strcmp(argv[i], "-bench")) {
-            opts->bench = 1;
-            opts->iters = atoi(argv[i + 1]);
-            opts->iters = FFMAX(opts->iters, 1);
+            int iters = atoi(argv[i + 1]);
+            if (iters <= 0) {
+                opts->bench = 0;
+                opts->iters = 1;
+            } else {
+                opts->bench = 1;
+                opts->iters = iters;
+            }
         } else if (!strcmp(argv[i], "-flags")) {
             SwsContext *dummy = sws_alloc_context();
             const AVOption *flags_opt = av_opt_find(dummy, "sws_flags", NULL, 
0, 0);
-- 
2.52.0


>From e91bd662d53c328b092cd832c39f01b54fae5cac Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 22:12:26 +0100
Subject: [PATCH 27/31] swscale/tests/swscale: always print loss in output

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 3aaa69751c..2a070effe4 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -306,8 +306,9 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
            mode->flags, mode->dither);
 
-    printf("SSIM {Y=%f U=%f V=%f A=%f}",
-           r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3]);
+    printf("SSIM {Y=%f U=%f V=%f A=%f} loss=%e",
+           r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3],
+           r->loss);
 
     if (opts->bench && ref_r->time) {
         double ratio = (double) ref_r->time / r->time;
@@ -579,11 +580,12 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
 
         ret = sscanf(buf,
                      "%20s %dx%d -> %20s %dx%d, flags=0x%x dither=%u, "
-                     "SSIM {Y=%f U=%f V=%f A=%f}\n",
+                     "SSIM {Y=%f U=%f V=%f A=%f} loss=%e\n",
                      src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh,
                      &mode.flags, &mode.dither,
-                     &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3]);
-        if (ret != 12) {
+                     &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3],
+                     &r.loss);
+        if (ret != 13) {
             av_log(NULL, AV_LOG_FATAL,
                    "Malformed reference file in line %d\n", line);
             goto error;
-- 
2.52.0


>From cf895a5f5913cdc6f6605f75dfc3b85778408ef5 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 22:17:27 +0100
Subject: [PATCH 28/31] swscale/tests/swscale: some tweaks to the output format

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 2a070effe4..6b2a7b2b41 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -301,12 +301,12 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
                           const struct test_results *ref_r,
                           float expected_loss)
 {
-    printf("%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u, ",
+    printf("%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u",
            av_get_pix_fmt_name(src->format), src->width, src->height,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
            mode->flags, mode->dither);
 
-    printf("SSIM {Y=%f U=%f V=%f A=%f} loss=%e",
+    printf(", SSIM={Y=%f U=%f V=%f A=%f} loss=%e",
            r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3],
            r->loss);
 
@@ -320,12 +320,12 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
         }
 
         if (av_log_get_level() >= AV_LOG_INFO) {
-            printf(" time=%"PRId64" us, ref=%"PRId64" us, speedup=%.3fx 
%s%s\033[0m",
+            printf(", time=%"PRId64" us (ref=%"PRId64" us), speedup=%.3fx 
%s%s\033[0m",
                    r->time / opts->iters, ref_r->time / opts->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
     } else if (opts->bench) {
-        printf(" time=%"PRId64" us", r->time / opts->iters);
+        printf(", time=%"PRId64" us", r->time / opts->iters);
     }
     printf("\n");
 
@@ -345,7 +345,7 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
         const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
         const char *worse_str = bad ? "WORSE" : "worse";
         av_log(NULL, level,
-               "  loss %e is %s by %e, ref loss %e SSIM {Y=%f U=%f V=%f 
A=%f}\n",
+               "  loss %e is %s by %e, ref loss %e SSIM={Y=%f U=%f V=%f 
A=%f}\n",
                r->loss, worse_str, r->loss - ref_r->loss, ref_r->loss,
                ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], ref_r->ssim[3]);
     }
@@ -580,7 +580,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
 
         ret = sscanf(buf,
                      "%20s %dx%d -> %20s %dx%d, flags=0x%x dither=%u, "
-                     "SSIM {Y=%f U=%f V=%f A=%f} loss=%e\n",
+                     "SSIM={Y=%f U=%f V=%f A=%f} loss=%e\n",
                      src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh,
                      &mode.flags, &mode.dither,
                      &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3],
-- 
2.52.0


>From aa1ee7a9cb3a488651f204bcfaa0a78179405829 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 22:23:21 +0100
Subject: [PATCH 29/31] swscale/tests/swscale: print number of iterations in
 benchmark output

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 6b2a7b2b41..c62590bd91 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -320,12 +320,12 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
         }
 
         if (av_log_get_level() >= AV_LOG_INFO) {
-            printf(", time=%"PRId64" us (ref=%"PRId64" us), speedup=%.3fx 
%s%s\033[0m",
-                   r->time / opts->iters, ref_r->time / opts->iters, ratio,
+            printf(", time=%"PRId64"/%u us (ref=%"PRId64"/%u us), 
speedup=%.3fx %s%s\033[0m",
+                   r->time, opts->iters, ref_r->time, opts->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
     } else if (opts->bench) {
-        printf(", time=%"PRId64" us", r->time / opts->iters);
+        printf(", time=%"PRId64"/%u us", r->time, opts->iters);
     }
     printf("\n");
 
-- 
2.52.0


>From f7992d65e9bc6497f6f5debc079b929950aa9e9a Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 20:11:02 +0100
Subject: [PATCH 30/31] swscale/tests/swscale: require reference file to
 perform comparisons

The legacy scaler is no longer implicitly used to generate a reference
to perform comparisons for every conversion. It is now up to the user
to generate a reference file and use it as input for a separate run to
perform comparisons.

It is now possible to compare against previous runs of the graph-based
scaler, for example to test for newer optimizations.

This reduces the overall time necessary to obtain speedup numbers from
the legacy scaler to the graph-based scaler (or any other comparison,
for that matter) since the reference must only be run once.

For example, to check the speedup between the legacy scaler and the
graph-based scaler:
  ./libswscale/tests/swscale [...] -bench 50 -legacy 1 > legacy_ref.txt
  ./libswscale/tests/swscale [...] -bench 50 -ref legacy_ref.txt

If no -ref file is specified, we are assuming that we are generating a
reference file, and therefore all information is printed (including
ssim/loss, and benchmarks if -bench is used).

If a -ref file is specified, the output printed depends on whether we
are testing for correctness (ssim/loss only) or benchmarking (time/
speedup only, along with overall speedup).

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 97 +++++++++++++++++---------------------
 1 file changed, 42 insertions(+), 55 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index c62590bd91..0128a9875c 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -64,6 +64,7 @@ struct test_results {
     float ssim[4];
     float loss;
     int64_t time;
+    int iters;
 };
 
 const SwsFlags flags[] = {
@@ -306,26 +307,30 @@ static void print_results(const AVFrame *ref, AVFrame 
*src, AVFrame *dst,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
            mode->flags, mode->dither);
 
-    printf(", SSIM={Y=%f U=%f V=%f A=%f} loss=%e",
-           r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3],
-           r->loss);
+    if (!opts->bench || !ref_r) {
+        printf(", SSIM={Y=%f U=%f V=%f A=%f} loss=%e",
+               r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3],
+               r->loss);
+        if (ref_r)
+            printf(" (ref=%e)", ref_r->loss);
+    }
 
-    if (opts->bench && ref_r->time) {
-        double ratio = (double) ref_r->time / r->time;
-        if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with low 
precision */) {
-            speedup_min = FFMIN(speedup_min, ratio);
-            speedup_max = FFMAX(speedup_max, ratio);
-            speedup_logavg += log(ratio);
-            speedup_count++;
-        }
+    if (opts->bench) {
+        printf(", time=%"PRId64"/%u us", r->time, opts->iters);
+        if (ref_r) {
+            double ratio = ((double) ref_r->time / ref_r->iters)
+                         / ((double) r->time / opts->iters);
+            if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with 
low precision */) {
+                speedup_min = FFMIN(speedup_min, ratio);
+                speedup_max = FFMAX(speedup_max, ratio);
+                speedup_logavg += log(ratio);
+                speedup_count++;
+            }
 
-        if (av_log_get_level() >= AV_LOG_INFO) {
-            printf(", time=%"PRId64"/%u us (ref=%"PRId64"/%u us), 
speedup=%.3fx %s%s\033[0m",
-                   r->time, opts->iters, ref_r->time, opts->iters, ratio,
+            printf(" (ref=%"PRId64"/%u us), speedup=%.3fx %s%s\033[0m",
+                   ref_r->time, ref_r->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
-    } else if (opts->bench) {
-        printf(", time=%"PRId64"/%u us", r->time, opts->iters);
     }
     printf("\n");
 
@@ -340,7 +345,7 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
                r->loss, worse_str, r->loss - expected_loss, expected_loss);
     }
 
-    if (!isnan(ref_r->loss) && r->loss - ref_r->loss > 1e-4) {
+    if (ref_r && r->loss - ref_r->loss > 1e-4) {
         const int bad = r->loss - ref_r->loss > 1e-2;
         const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
         const char *worse_str = bad ? "WORSE" : "worse";
@@ -369,7 +374,8 @@ static int init_frame(AVFrame **pframe, const AVFrame *ref,
 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
                     int dst_w, int dst_h,
                     const struct mode *mode, const struct options *opts,
-                    const AVFrame *ref, AVFrame *src, const float ssim_ref[4])
+                    const AVFrame *ref, AVFrame *src,
+                    struct test_results *ref_r)
 {
     AVFrame *dst = NULL, *out = NULL;
     const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
@@ -387,7 +393,6 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
     const float expected_loss = get_loss(ssim_expected);
 
     struct test_results r;
-    struct test_results ref_r = { .loss = NAN };
 
     if (src->format != src_fmt) {
         av_frame_unref(src);
@@ -438,34 +443,7 @@ static int run_test(enum AVPixelFormat src_fmt, enum 
AVPixelFormat dst_fmt,
         goto early_exit;
     }
 
-    if (!ssim_ref || opts->bench) {
-        /* Compare against the legacy swscale API as a reference */
-        ret = scale_legacy(dst, src, mode, opts, &ref_r.time);
-        if (ret < 0)
-            goto error;
-
-        ret = checked_sws_scale_frame(sws_dst_out, out, dst);
-        if (ret < 0)
-            goto error;
-
-        get_ssim(ref_r.ssim, out, ref, comps);
-
-        /* Legacy swscale does not perform bit accurate upconversions of low
-         * bit depth RGB. This artificially improves the SSIM score because the
-         * resulting error deletes some of the input dither noise. This gives
-         * it an unfair advantage when compared against a bit exact reference.
-         * Work around this by ensuring that the reference SSIM score is not
-         * higher than it theoretically "should" be. */
-        if (src_var > dst_var) {
-            const float src_loss = (2 * ref_var + c1) / (2 * ref_var + src_var 
+ c1);
-            ref_r.ssim[0] = FFMIN(ref_r.ssim[0], src_loss);
-        }
-    } else {
-        memcpy(ref_r.ssim, ssim_ref, sizeof(ref_r.ssim));
-    }
-
-    ref_r.loss = get_loss(ref_r.ssim);
-    if (r.loss - ref_r.loss > 1e-2) {
+    if (ref_r && r.loss - ref_r->loss > 1e-2) {
         ret = -1;
         goto early_exit;
     }
@@ -476,7 +454,7 @@ early_exit:
     print_results(ref, src, dst,
                   dst_w, dst_h,
                   mode, opts,
-                  &r, &ref_r,
+                  &r, ref_r,
                   expected_loss);
 
  error:
@@ -572,24 +550,33 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
         enum AVPixelFormat src_fmt;
         enum AVPixelFormat dst_fmt;
         int sw, sh, dw, dh;
-        struct test_results r;
+        struct test_results r = { 0 };
         struct mode mode;
-
-        if (!strncmp(buf, "Overall speedup", 15))
-            continue;
+        int n = 0;
 
         ret = sscanf(buf,
                      "%20s %dx%d -> %20s %dx%d, flags=0x%x dither=%u, "
-                     "SSIM={Y=%f U=%f V=%f A=%f} loss=%e\n",
+                     "SSIM={Y=%f U=%f V=%f A=%f} loss=%e%n",
                      src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh,
                      &mode.flags, &mode.dither,
                      &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3],
-                     &r.loss);
+                     &r.loss, &n);
         if (ret != 13) {
             av_log(NULL, AV_LOG_FATAL,
                    "Malformed reference file in line %d\n", line);
             goto error;
         }
+        if (opts->bench) {
+            ret = sscanf(buf + n,
+                         ", time=%"PRId64"/%u us",
+                         &r.time, &r.iters);
+            if (ret != 2) {
+                av_log(NULL, AV_LOG_FATAL,
+                       "Missing benchmarks from reference file in line %d\n",
+                       line);
+                goto error;
+            }
+        }
 
         src_fmt = av_get_pix_fmt(src_fmt_str);
         dst_fmt = av_get_pix_fmt(dst_fmt_str);
@@ -611,7 +598,7 @@ static int run_file_tests(const AVFrame *ref, FILE *fp, 
const struct options *op
             opts->dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts->dst_fmt)
             continue;
 
-        ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, 
r.ssim);
+        ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, &r);
         if (ret < 0)
             goto error;
     }
-- 
2.52.0


>From 26ba16af1201e2403e30317f4d1b97a7ab199a9f Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 7 Mar 2026 22:52:13 +0100
Subject: [PATCH 31/31] swscale/tests/swscale: print aligned output

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/tests/swscale.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libswscale/tests/swscale.c b/libswscale/tests/swscale.c
index 0128a9875c..8aa6705170 100644
--- a/libswscale/tests/swscale.c
+++ b/libswscale/tests/swscale.c
@@ -302,7 +302,7 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
                           const struct test_results *ref_r,
                           float expected_loss)
 {
-    printf("%s %dx%d -> %s %3dx%3d, flags=0x%x dither=%u",
+    printf("%-14s %4dx%4d -> %-14s %4dx%4d, flags=0x%08x dither=%u",
            av_get_pix_fmt_name(src->format), src->width, src->height,
            av_get_pix_fmt_name(dst->format), dst->width, dst->height,
            mode->flags, mode->dither);
@@ -316,7 +316,7 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
     }
 
     if (opts->bench) {
-        printf(", time=%"PRId64"/%u us", r->time, opts->iters);
+        printf(", time=%7"PRId64"/%u us", r->time, opts->iters);
         if (ref_r) {
             double ratio = ((double) ref_r->time / ref_r->iters)
                          / ((double) r->time / opts->iters);
@@ -327,7 +327,7 @@ static void print_results(const AVFrame *ref, AVFrame *src, 
AVFrame *dst,
                 speedup_count++;
             }
 
-            printf(" (ref=%"PRId64"/%u us), speedup=%.3fx %s%s\033[0m",
+            printf(" (ref=%7"PRId64"/%u us), speedup=%6.3fx %s%s\033[0m",
                    ref_r->time, ref_r->iters, ratio,
                    speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
         }
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to