PR #25542 opened by Ramiro Polla (ramiro) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25542 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25542.patch
Based on suggestion from https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25387#issuecomment-75633 >From 83cb47e3845b1856e443a03137a1c094ca6c707f Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 27 Oct 2026 18:00:34 +0100 Subject: [PATCH 1/5] swscale/ops_memcpy: load per-plane pointers and strides once This makes the code easier to read and will let us do a few more simplifications. --- libswscale/ops_memcpy.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/libswscale/ops_memcpy.c b/libswscale/ops_memcpy.c index 735006ab188..a079d8fcc4a 100644 --- a/libswscale/ops_memcpy.c +++ b/libswscale/ops_memcpy.c @@ -46,30 +46,32 @@ static void process(const SwsOpExec *exec, const void *priv, av_assert1(x_start == 0 && x_end == exec->width); for (int i = 0; i < p->num_planes; i++) { - uint8_t *out = exec->out[i]; - const int idx = p->index[i]; + uint8_t *out = exec->out[i]; + const ptrdiff_t out_stride = exec->out_stride[i]; + const int idx = p->index[i]; + const uint8_t *in = (idx >= 0) ? exec->in[idx] : NULL; + const ptrdiff_t in_stride = (idx >= 0) ? exec->in_stride[idx] : 0; const int bytes = x_end * exec->block_size_out[i]; - const int use_loop = exec->out_stride[i] < 0 || - exec->out_stride[i] > bytes + SWS_MAX_PADDING; - const size_t span = (size_t) (lines - 1) * exec->out_stride[i] + bytes; - if (idx < 0 && !use_loop) { + const int use_loop = out_stride < 0 || + out_stride > bytes + SWS_MAX_PADDING; + const size_t span = (size_t) (lines - 1) * out_stride + bytes; + if (!in && !use_loop) { memset(out, p->clear_value[i], span); - } else if (idx < 0) { + } else if (!in) { for (int y = y_start; y < y_end; y++) { memset(out, p->clear_value[i], bytes); - out += exec->out_stride[i]; + out += out_stride; } - } else if (out == exec->in[idx]) { - av_assert1(exec->out_stride[i] == exec->in_stride[idx]); + } else if (out == in) { + av_assert1(out_stride == in_stride); continue; /* plane was already ref'd */ - } else if (exec->out_stride[i] == exec->in_stride[idx] && !use_loop) { - memcpy(out, exec->in[idx], span); + } else if (out_stride == in_stride && !use_loop) { + memcpy(out, in, span); } else { - const uint8_t *in = exec->in[idx]; for (int y = y_start; y < y_end; y++) { memcpy(out, in, bytes); - out += exec->out_stride[i]; - in += exec->in_stride[idx]; + out += out_stride; + in += in_stride; } } } -- 2.54.0 >From 23ec704afbcbabf2818999118ba9d5d2aab7a088 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 27 Oct 2026 19:05:37 +0100 Subject: [PATCH 2/5] swscale/ops_memcpy: move early-skip block before unrelated variable declarations --- libswscale/ops_memcpy.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libswscale/ops_memcpy.c b/libswscale/ops_memcpy.c index a079d8fcc4a..6bcc05b0cd9 100644 --- a/libswscale/ops_memcpy.c +++ b/libswscale/ops_memcpy.c @@ -51,6 +51,12 @@ static void process(const SwsOpExec *exec, const void *priv, const int idx = p->index[i]; const uint8_t *in = (idx >= 0) ? exec->in[idx] : NULL; const ptrdiff_t in_stride = (idx >= 0) ? exec->in_stride[idx] : 0; + + if (out == in) { + av_assert1(out_stride == in_stride); + continue; /* plane was already ref'd */ + } + const int bytes = x_end * exec->block_size_out[i]; const int use_loop = out_stride < 0 || out_stride > bytes + SWS_MAX_PADDING; @@ -62,9 +68,6 @@ static void process(const SwsOpExec *exec, const void *priv, memset(out, p->clear_value[i], bytes); out += out_stride; } - } else if (out == in) { - av_assert1(out_stride == in_stride); - continue; /* plane was already ref'd */ } else if (out_stride == in_stride && !use_loop) { memcpy(out, in, span); } else { -- 2.54.0 >From a80350b6c8d45ae1e430d430beee96a9c02636d0 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 27 Oct 2026 19:05:54 +0100 Subject: [PATCH 3/5] swscale/ops_memcpy: merge bulk memset/memcpy paths into per-line loops Instead of having separate paths for bulk and per-line memset/memcpy operations, modify the loop count and size parameters. --- libswscale/ops_memcpy.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/libswscale/ops_memcpy.c b/libswscale/ops_memcpy.c index 6bcc05b0cd9..9e73554ddf2 100644 --- a/libswscale/ops_memcpy.c +++ b/libswscale/ops_memcpy.c @@ -42,7 +42,6 @@ static void process(const SwsOpExec *exec, const void *priv, int x_start, int y_start, int x_end, int y_end) { const MemcpyPriv *p = priv; - const int lines = y_end - y_start; av_assert1(x_start == 0 && x_end == exec->width); for (int i = 0; i < p->num_planes; i++) { @@ -57,21 +56,23 @@ static void process(const SwsOpExec *exec, const void *priv, continue; /* plane was already ref'd */ } - const int bytes = x_end * exec->block_size_out[i]; - const int use_loop = out_stride < 0 || - out_stride > bytes + SWS_MAX_PADDING; - const size_t span = (size_t) (lines - 1) * out_stride + bytes; - if (!in && !use_loop) { - memset(out, p->clear_value[i], span); - } else if (!in) { - for (int y = y_start; y < y_end; y++) { + size_t bytes = x_end * exec->block_size_out[i]; + int lines = y_end - y_start; + + /* Replace the per-line loop with a bulk memset/memcpy when possible. */ + if ((!in || out_stride == in_stride) && + out_stride >= 0 && (size_t) out_stride <= bytes + SWS_MAX_PADDING) { + bytes += (size_t) (lines - 1) * out_stride; + lines = 1; + } + + if (!in) { + for (int y = 0; y < lines; y++) { memset(out, p->clear_value[i], bytes); out += out_stride; } - } else if (out_stride == in_stride && !use_loop) { - memcpy(out, in, span); } else { - for (int y = y_start; y < y_end; y++) { + for (int y = 0; y < lines; y++) { memcpy(out, in, bytes); out += out_stride; in += in_stride; -- 2.54.0 >From 85823292ab39231036b8e184d766249ca6007cd2 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 27 Oct 2026 19:55:14 +0100 Subject: [PATCH 4/5] swscale/ops_memcpy: also use bulk memcpy/memset for negative strides Just modify the base input/output pointers, the rest of the code has already been generalized to support this. --- libswscale/ops_memcpy.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libswscale/ops_memcpy.c b/libswscale/ops_memcpy.c index 9e73554ddf2..f90e875b9e3 100644 --- a/libswscale/ops_memcpy.c +++ b/libswscale/ops_memcpy.c @@ -60,9 +60,16 @@ static void process(const SwsOpExec *exec, const void *priv, int lines = y_end - y_start; /* Replace the per-line loop with a bulk memset/memcpy when possible. */ + const size_t abs_stride = FFABS(out_stride); if ((!in || out_stride == in_stride) && - out_stride >= 0 && (size_t) out_stride <= bytes + SWS_MAX_PADDING) { - bytes += (size_t) (lines - 1) * out_stride; + abs_stride <= bytes + SWS_MAX_PADDING) { + if (out_stride < 0) { + const ptrdiff_t offset = (lines - 1) * out_stride; + out += offset; + if (in) + in += offset; + } + bytes += (lines - 1) * abs_stride; lines = 1; } -- 2.54.0 >From 61be04d3e5f5a41b06ac67c094755de37c9eedd5 Mon Sep 17 00:00:00 2001 From: Ramiro Polla <[email protected]> Date: Tue, 27 Oct 2026 20:03:32 +0100 Subject: [PATCH 5/5] fate/libswscale: test all unscaled conversions with the memcpy backend The memcpy backend only supports unscaled conversions, so we can set the -scaler to none while testing it. We can then also clear the -p parameter, to run all tests. --- tests/fate/libswscale.mak | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/fate/libswscale.mak b/tests/fate/libswscale.mak index 802ac72a03e..ae427e46ac3 100644 --- a/tests/fate/libswscale.mak +++ b/tests/fate/libswscale.mak @@ -81,16 +81,24 @@ fate-sws-unstable: $(FATE_SWS_UNSTABLE) SWS_CPU_BACKENDS := $(filter-out spirv,$(SWS_UNSTABLE_BACKENDS-yes)) FATE_SWS_UNPADDED := $(SWS_CPU_BACKENDS:%=fate-sws-unpadded-%) $(FATE_SWS_UNPADDED): libswscale/tests/swscale$(EXESUF) -$(FATE_SWS_UNPADDED): CMD = run libswscale/tests/swscale$(EXESUF) -v 16 -backends $(@:fate-sws-unpadded-%=%) -align_src 1 -align_dst 1 -p 0.01 +$(FATE_SWS_UNPADDED): CMD = run libswscale/tests/swscale$(EXESUF) -v 16 -backends $(@:fate-sws-unpadded-%=%) -align_src 1 -align_dst 1 $(SWS_PROB) $(SWS_SCALER) $(FATE_SWS_UNPADDED): REF = /dev/null fate-sws-unpadded: $(FATE_SWS_UNPADDED) FATE_SWS_FLIPPED := $(SWS_CPU_BACKENDS:%=fate-sws-flipped-%) $(FATE_SWS_FLIPPED): libswscale/tests/swscale$(EXESUF) -$(FATE_SWS_FLIPPED): CMD = run libswscale/tests/swscale$(EXESUF) -v 16 -backends $(@:fate-sws-flipped-%=%) -align_src -1 -align_dst -1 -p 0.01 +$(FATE_SWS_FLIPPED): CMD = run libswscale/tests/swscale$(EXESUF) -v 16 -backends $(@:fate-sws-flipped-%=%) -align_src -1 -align_dst -1 $(SWS_PROB) $(SWS_SCALER) $(FATE_SWS_FLIPPED): REF = /dev/null fate-sws-flipped: $(FATE_SWS_FLIPPED) +# Run only 1% of the tests +$(FATE_SWS_UNPADDED) $(FATE_SWS_FLIPPED): SWS_PROB = -p 0.01 + +# The memcpy backend only supports unscaled conversions, so test all of +# those instead +fate-sws-unpadded-memcpy fate-sws-flipped-memcpy: SWS_PROB = +fate-sws-unpadded-memcpy fate-sws-flipped-memcpy: SWS_SCALER = -scaler none + FATE_LIBSWSCALE += $(FATE_SWS_UNSCALED) $(FATE_SWS_UNSTABLE) $(FATE_SWS_UNPADDED) $(FATE_SWS_FLIPPED) endif -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
