PR #22538 opened by mkver
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22538
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22538.patch


>From a6fce3701a726caed870b65d9c95673419115327 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Mon, 16 Mar 2026 22:44:57 +0100
Subject: [PATCH 1/5] avcodec/x86/mpeg4videodsp: Fix sprite_warping_accuracy
 0-2

MPEG-4 GMC uses the following motion prediction scheme:
For output pixel (x,y), the reference pixel at fractional
coordinates (ox+dxx*x+dxy*y,oy+dyx*x+dyy*y) is used as prediction;
the latter is calculated via bilinear interpolation. The coefficients
here are fixed-point values with 16+shift fractional bits
where shift is sprite_warping_accuracy+1. For the weights,
only the shift most significant fractional bits are used.
shift can be at most four*.

The x86 MMX gmc implementation performs these calculations
using 16-bit words. To do so, it restricts itself to the case
in which the four least significant bits of dxx,dxy,dyx,dyy
are zero and shifts these bits away. Yet in case shift is
less than four, the 16 bits retained also contain at least
one bit that actually belongs to the fpel component
(which is already taken into account by using the correct
pixels for interpolation).

(This has been uncovered by a to-be-added checkasm test.
I don't know whether there are actual files in the wild
using sprite_warping_accuracy 0-2.)

*: It is always four when encoding with xvid and GMC.

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libavcodec/x86/mpeg4videodsp.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/libavcodec/x86/mpeg4videodsp.c b/libavcodec/x86/mpeg4videodsp.c
index 6a1c6c5064..a5984ed120 100644
--- a/libavcodec/x86/mpeg4videodsp.c
+++ b/libavcodec/x86/mpeg4videodsp.c
@@ -33,12 +33,16 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
     const int w    = 8;
     const int ix   = ox  >> (16 + shift);
     const int iy   = oy  >> (16 + shift);
-    const int oxs  = ox  >> 4;
-    const int oys  = oy  >> 4;
-    const int dxxs = dxx >> 4;
+    const int ox2  = ox & (1 << (16 + shift)) - 1;
+    const int oy2  = oy & (1 << (16 + shift)) - 1;
+    const int oxs  = ox2 >> 4;
+    const int oys  = oy2 >> 4;
+    const int dxx2 = dxx - (1 << (16 + shift));
+    const int dyy2 = dyy - (1 << (16 + shift));
+    const int dxxs = dxx2 >> 4;
     const int dxys = dxy >> 4;
     const int dyxs = dyx >> 4;
-    const int dyys = dyy >> 4;
+    const int dyys = dyy2 >> 4;
     const uint16_t r4[4]   = { r, r, r, r };
     const uint16_t dxy4[4] = { dxys, dxys, dxys, dxys };
     const uint16_t dyy4[4] = { dyys, dyys, dyys, dyys };
@@ -48,8 +52,8 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
     uint8_t edge_buf[(MAX_H + 1) * MAX_STRIDE];
     int x, y;
 
-    const int dxw = (dxx - (1 << (16 + shift))) * (w - 1);
-    const int dyh = (dyy - (1 << (16 + shift))) * (h - 1);
+    const int dxw = dxx2 * (w - 1);
+    const int dyh = dyy2 * (h - 1);
     const int dxh = dxy * (h - 1);
     const int dyw = dyx * (w - 1);
     int need_emu  =  (unsigned) ix >= width  - w || width < w ||
@@ -57,8 +61,8 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
                      ;
 
     if ( // non-constant fullpel offset (3% of blocks)
-        ((ox ^ (ox + dxw)) | (ox ^ (ox + dxh)) | (ox ^ (ox + dxw + dxh)) |
-         (oy ^ (oy + dyw)) | (oy ^ (oy + dyh)) | (oy ^ (oy + dyw + dyh))) >> 
(16 + shift) ||
+        ((ox2 + dxw) | (ox2 + dxh) | (ox2 + dxw + dxh) |
+         (oy2 + dyw) | (oy2 + dyh) | (oy2 + dyw + dyh)) >> (16 + shift) ||
         // uses more than 16 bits of subpel mv (only at huge resolution)
         (dxx | dxy | dyx | dyy) & 15 ||
         (need_emu && (h > MAX_H || stride > MAX_STRIDE))) {
-- 
2.52.0


>From 3a32f4e937c6aba81416d9bf126da02350b0f507 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Tue, 17 Mar 2026 05:33:13 +0100
Subject: [PATCH 2/5] tests/checkasm: Add test for mpeg4videodsp

It already uncovered a bug in the MMX version of gmc.

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 tests/checkasm/Makefile        |   1 +
 tests/checkasm/checkasm.c      |   3 +
 tests/checkasm/checkasm.h      |   1 +
 tests/checkasm/mpeg4videodsp.c | 156 +++++++++++++++++++++++++++++++++
 tests/fate/checkasm.mak        |   1 +
 5 files changed, 162 insertions(+)
 create mode 100644 tests/checkasm/mpeg4videodsp.c

diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
index 1e23587de9..6c525356aa 100644
--- a/tests/checkasm/Makefile
+++ b/tests/checkasm/Makefile
@@ -41,6 +41,7 @@ AVCODECOBJS-$(CONFIG_DIRAC_DECODER)     += diracdsp.o
 AVCODECOBJS-$(CONFIG_EXR_DECODER)       += exrdsp.o
 AVCODECOBJS-$(CONFIG_FLAC_DECODER)      += flacdsp.o
 AVCODECOBJS-$(CONFIG_JPEG2000_DECODER)  += jpeg2000dsp.o
+AVCODECOBJS-$(CONFIG_MPEG4_DECODER)     += mpeg4videodsp.o
 AVCODECOBJS-$(CONFIG_OPUS_DECODER)      += opusdsp.o
 AVCODECOBJS-$(CONFIG_PIXBLOCKDSP)       += pixblockdsp.o
 AVCODECOBJS-$(CONFIG_HEVC_DECODER)      += hevc_add_res.o hevc_deblock.o 
hevc_dequant.o hevc_idct.o hevc_sao.o hevc_pel.o
diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c
index 8b2f648b3d..b1504b14bb 100644
--- a/tests/checkasm/checkasm.c
+++ b/tests/checkasm/checkasm.c
@@ -222,6 +222,9 @@ static const struct {
     #if CONFIG_ME_CMP
         { "motion", checkasm_check_motion },
     #endif
+    #if CONFIG_MPEG4_DECODER
+        { "mpeg4videodsp", checkasm_check_mpeg4videodsp },
+    #endif
     #if CONFIG_MPEGVIDEO
         { "mpegvideo_unquantize", checkasm_check_mpegvideo_unquantize },
     #endif
diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h
index 25654b20ba..f653207363 100644
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -126,6 +126,7 @@ void checkasm_check_llviddsp(void);
 void checkasm_check_llvidencdsp(void);
 void checkasm_check_lpc(void);
 void checkasm_check_motion(void);
+void checkasm_check_mpeg4videodsp(void);
 void checkasm_check_mpegvideo_unquantize(void);
 void checkasm_check_mpegvideoencdsp(void);
 void checkasm_check_nlmeans(void);
diff --git a/tests/checkasm/mpeg4videodsp.c b/tests/checkasm/mpeg4videodsp.c
new file mode 100644
index 0000000000..79a3ac5805
--- /dev/null
+++ b/tests/checkasm/mpeg4videodsp.c
@@ -0,0 +1,156 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+
+#include "checkasm.h"
+#include "libavcodec/mpeg4videodsp.h"
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem_internal.h"
+
+enum {
+    MAX_WIDTH        = 1024,
+    MAX_HEIGHT       = 64,
+    MAX_STRIDE       = MAX_WIDTH,
+    MAX_BLOCK_HEIGHT = 16,
+    W                = 8,
+};
+
+static_assert(MAX_WIDTH <= MAX_STRIDE, "stride needs to be >= width");
+
+#define randomize_buffer(buf)                                      \
+    do {                                                           \
+        static_assert(!(sizeof(buf) % 4), "Tail handling needed"); \
+        for (size_t k = 0; k < sizeof(buf); k += 4) {              \
+            uint32_t r = rnd();                                    \
+            AV_WN32A(buf + k, r);                                  \
+        }                                                          \
+    } while (0)
+
+static int get_signed_rnd(int nb_bits)
+{
+    int32_t r = rnd();
+    return r >> (32 - nb_bits);
+}
+
+static int get_mv_delta(int shift, int is_diag)
+{
+    // The coordinates of the motion vector differences are fixed point numbers
+    // whose fractional part has 16+shift bits. We use 5+shift+4 bit mantissa
+    // for the deviation from the normal, so that the absolute value 
corresponds
+    // to < 2^(-7). For height 16, the maximum absolute deviation is < 1/8.
+    // Additionally, we always use zero for the four least significant bits,
+    // as the x86 implementation always falls back to the C one if it is not 
so.
+    return get_signed_rnd(6 + shift) * 16 + (is_diag ? (1 << (16 + shift)) : 
0);
+}
+
+static int modify_fpel(int coordinate, int size, int block_size, int type)
+{
+    switch (type) {
+    default: av_unreachable("impossible");
+    // fallthrough
+    case 2: return coordinate; // do nothing
+    // modify coordinate so that it requires pixel replication to the left/top
+    case 1: return coordinate % block_size - block_size;
+    // modify coordinate so that it requires pixel replication to the 
right/down
+    case 0: return coordinate + block_size + (size - (block_size + 1) - 
coordinate) / block_size * block_size;
+    }
+}
+
+static void checkasm_check_gmc(const Mpeg4VideoDSPContext *const mdsp)
+{
+    DECLARE_ALIGNED_8(uint8_t, buf_new)[MAX_BLOCK_HEIGHT * MAX_STRIDE];
+    DECLARE_ALIGNED_8(uint8_t, buf_ref)[MAX_BLOCK_HEIGHT * MAX_STRIDE];
+    DECLARE_ALIGNED_4(uint8_t, srcbuf)[MAX_STRIDE * MAX_HEIGHT];
+
+    declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *dst, const uint8_t *src,
+                 int stride, int h, int ox, int oy,
+                 int dxx, int dxy, int dyx, int dyy,
+                 int shift, int r, int width, int height);
+
+    randomize_buffer(srcbuf);
+    randomize_buffer(buf_ref);
+    memcpy(buf_new, buf_ref, sizeof(buf_new));
+
+    int shift = 1 + rnd() % 4; // range 1..4
+    const int h = rnd() & 1 ? 16 : 8;
+    const int r = (1 << (2 * shift - 1)) - (rnd() & 1);
+    const int width  = FFALIGN(W + rnd() % (MAX_WIDTH - W + 1), 16);  // range 
8..MAX_WIDTH
+    const int height = FFALIGN(h + rnd() % (MAX_HEIGHT - h + 1), 8); // range 
h..MAX_HEIGHT
+    ptrdiff_t stride = FFALIGN(width + rnd() % (MAX_STRIDE - width + 1), 8);
+    const uint8_t *src = srcbuf;
+    uint8_t *dst_new = buf_new, *dst_ref = buf_ref;
+
+    if (rnd() & 1) { // negate stride
+        dst_new += stride * (h - 1);
+        dst_ref += stride * (h - 1);
+        src     += stride * (height - 1);
+        stride  *= -1;
+    }
+    // Get the fullpel component of the motion vector.
+    // Restrict the range so that a (W+1)x(h+1) buffer fits in srcbuf
+    // (if possible) in order to test the non-edge-emulation codepath.
+    int fpel_x = width  == W ? 0 : rnd() % (width  - W);
+    int fpel_y = height == h ? 0 : rnd() % (height - h);
+    int dxx = get_mv_delta(shift, 1), dxy = get_mv_delta(shift, 0);
+    int dyx = get_mv_delta(shift, 0), dyy = get_mv_delta(shift, 1);
+
+    int ox  = fpel_x << (16 + shift) | rnd() & ((1 << (16 + shift)) - 1);
+    int oy  = fpel_y << (16 + shift) | rnd() & ((1 << (16 + shift)) - 1);
+
+    call_ref(dst_ref, src, stride, h, ox, oy,
+             dxx, dxy, dyx, dyy, shift, r, width, height);
+    call_new(dst_new, src, stride, h, ox, oy,
+             dxx, dxy, dyx, dyy, shift, r, width, height);
+    if (memcmp(buf_new, buf_ref, sizeof(buf_new)))
+        fail();
+
+    bench_new(dst_new, src, stride, h, ox, oy,
+              dxx, dxy, dyx, dyy, shift, r, width, height);
+
+    // Now test the case of src being partially outside of the actual picture.
+    if (!check_func(mdsp->gmc, "gmc_edge_emulation"))
+        return; // shouldn't happen
+    int type = rnd() % 8;
+    fpel_x = modify_fpel(fpel_x, width,  8, type % 3);
+    fpel_y = modify_fpel(fpel_y, height, h, type / 3);
+    ox  = fpel_x * (1 << (16 + shift)) | rnd() & ((1 << (16 + shift)) - 1);
+    oy  = fpel_y * (1 << (16 + shift)) | rnd() & ((1 << (16 + shift)) - 1);
+    call_ref(dst_ref, src, stride, h, ox, oy,
+             dxx, dxy, dyx, dyy, shift, r, width, height);
+    call_new(dst_new, src, stride, h, ox, oy,
+             dxx, dxy, dyx, dyy, shift, r, width, height);
+    if (memcmp(buf_new, buf_ref, sizeof(buf_new)))
+        fail();
+
+    bench_new(dst_new, src, stride, h, ox, oy,
+              dxx, dxy, dyx, dyy, shift, r, width, height);
+}
+
+void checkasm_check_mpeg4videodsp(void)
+{
+    Mpeg4VideoDSPContext mdsp;
+
+    ff_mpeg4videodsp_init(&mdsp);
+
+    if (check_func(mdsp.gmc, "gmc")) {
+        checkasm_check_gmc(&mdsp);
+        report("gmc");
+    }
+}
diff --git a/tests/fate/checkasm.mak b/tests/fate/checkasm.mak
index bd44bfd536..8441bb3719 100644
--- a/tests/fate/checkasm.mak
+++ b/tests/fate/checkasm.mak
@@ -42,6 +42,7 @@ FATE_CHECKASM = fate-checkasm-aacencdsp                       
          \
                 fate-checkasm-llvidencdsp                               \
                 fate-checkasm-lpc                                       \
                 fate-checkasm-motion                                    \
+                fate-checkasm-mpeg4videodsp                             \
                 fate-checkasm-mpegvideo_unquantize                      \
                 fate-checkasm-mpegvideoencdsp                           \
                 fate-checkasm-opusdsp                                   \
-- 
2.52.0


>From 4b8f18acc083beec2c2de9216446a13c6b514f43 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Sat, 14 Mar 2026 09:24:36 +0100
Subject: [PATCH 3/5] avcodec/x86/mpeg4videodsp: Use smaller edge_emu buffer

edge_emu_mc allows to use different src and dst strides,
so one can replace the outsized edge emu buffer with
one that is much smaller and nevertheless big enough
for all our needs; it also avoids having to check
whether the buffer is actually big enough.

This also improves performance (if the compiler uses
stack probing). Old benchmarks:
gmc_c:                                                 814.5 ( 1.00x)
gmc_mmx:                                               243.7 ( 3.34x)

New benchmarks:
gmc_c:                                                 813.8 ( 1.00x)
gmc_mmx:                                               213.5 ( 3.81x)

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libavcodec/x86/mpeg4videodsp.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/libavcodec/x86/mpeg4videodsp.c b/libavcodec/x86/mpeg4videodsp.c
index a5984ed120..836eaa674d 100644
--- a/libavcodec/x86/mpeg4videodsp.c
+++ b/libavcodec/x86/mpeg4videodsp.c
@@ -30,6 +30,11 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
                     int dxx, int dxy, int dyx, int dyy,
                     int shift, int r, int width, int height)
 {
+    enum {
+        W               = 8,
+        EDGE_EMU_STRIDE = 16, //< anything >= W+1 will do
+        MAX_H           = 16,
+    };
     const int w    = 8;
     const int ix   = ox  >> (16 + shift);
     const int iy   = oy  >> (16 + shift);
@@ -47,9 +52,7 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
     const uint16_t dxy4[4] = { dxys, dxys, dxys, dxys };
     const uint16_t dyy4[4] = { dyys, dyys, dyys, dyys };
     const uint64_t shift2  = 2 * shift;
-#define MAX_STRIDE 4096U
-#define MAX_H 8U
-    uint8_t edge_buf[(MAX_H + 1) * MAX_STRIDE];
+    uint8_t edge_buf[(MAX_H + 1) * EDGE_EMU_STRIDE];
     int x, y;
 
     const int dxw = dxx2 * (w - 1);
@@ -64,18 +67,18 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
         ((ox2 + dxw) | (ox2 + dxh) | (ox2 + dxw + dxh) |
          (oy2 + dyw) | (oy2 + dyh) | (oy2 + dyw + dyh)) >> (16 + shift) ||
         // uses more than 16 bits of subpel mv (only at huge resolution)
-        (dxx | dxy | dyx | dyy) & 15 ||
-        (need_emu && (h > MAX_H || stride > MAX_STRIDE))) {
-        // FIXME could still use mmx for some of the rows
+        (dxx | dxy | dyx | dyy) & 15) {
         ff_gmc_c(dst, src, stride, h, ox, oy, dxx, dxy, dyx, dyy,
                  shift, r, width, height);
         return;
     }
 
     src += ix + iy * stride;
+    ptrdiff_t src_stride = stride;
     if (need_emu) {
-        ff_emulated_edge_mc_8(edge_buf, src, stride, stride, w + 1, h + 1, ix, 
iy, width, height);
-        src = edge_buf;
+        ff_emulated_edge_mc_8(edge_buf, src, EDGE_EMU_STRIDE, src_stride, w + 
1, h + 1, ix, iy, width, height);
+        src        = edge_buf;
+        src_stride = EDGE_EMU_STRIDE;
     }
 
     __asm__ volatile (
@@ -144,11 +147,11 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
 
                 : "=m" (dst[x + y * stride])
                 : "m" (src[0]), "m" (src[1]),
-                  "m" (src[stride]), "m" (src[stride + 1]),
+                  "m" (src[src_stride]), "m" (src[src_stride + 1]),
                   "m" (*r4), "m" (shift2));
-            src += stride;
+            src += src_stride;
         }
-        src += 4 - h * stride;
+        src += 4 - h * src_stride;
     }
 }
 
-- 
2.52.0


>From 5035acebe03fe74b3d66301701fdee52863d9154 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Sat, 14 Mar 2026 18:07:40 +0100
Subject: [PATCH 4/5] avcodec/x86/mpeg4videodsp: Add gmc_ssse3

It beats MMX by a lot, because it has to process eight words.
Also notice that the MMX code expects registers to be preserved
between separate inline assembly blocks which is not guaranteed;
the new code meanwhile does not presume this.

Benchmarks:
gmc_c:                                                 817.8 ( 1.00x)
gmc_mmx:                                               210.7 ( 3.88x)
gmc_ssse3:                                              80.7 (10.14x)

The MMX version has been removed.

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libavcodec/x86/mpeg4videodsp.c | 221 +++++++++++++++++++++------------
 tests/checkasm/mpeg4videodsp.c |   2 +-
 2 files changed, 140 insertions(+), 83 deletions(-)

diff --git a/libavcodec/x86/mpeg4videodsp.c b/libavcodec/x86/mpeg4videodsp.c
index 836eaa674d..f3f7036157 100644
--- a/libavcodec/x86/mpeg4videodsp.c
+++ b/libavcodec/x86/mpeg4videodsp.c
@@ -19,16 +19,27 @@
 #include "config.h"
 #include "libavutil/attributes.h"
 #include "libavutil/cpu.h"
+#include "libavutil/mem_internal.h"
+#include "libavutil/x86/asm.h"
 #include "libavutil/x86/cpu.h"
 #include "libavcodec/mpeg4videodsp.h"
 #include "libavcodec/videodsp.h"
 
-#if HAVE_INLINE_ASM
+#if HAVE_SSSE3_INLINE
 
-static void gmc_mmx(uint8_t *dst, const uint8_t *src,
-                    int stride, int h, int ox, int oy,
-                    int dxx, int dxy, int dyx, int dyy,
-                    int shift, int r, int width, int height)
+#define SPLATW(reg) "pshuflw  $0, %%" #reg ", %%" #reg "\n\t" \
+                    "punpcklqdq   %%" #reg ", %%" #reg "\n\t"
+
+typedef struct {
+    DECLARE_ALIGNED_16(uint16_t, u16)[8];
+} xmm_u16;
+
+DECLARE_ASM_CONST(16, xmm_u16, pw_0to7) = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
+
+static void gmc_ssse3(uint8_t *dst, const uint8_t *src,
+                      int stride, int h, int ox, int oy,
+                      int dxx, int dxy, int dyx, int dyy,
+                      int shift, int r, int width, int height)
 {
     enum {
         W               = 8,
@@ -48,12 +59,7 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
     const int dxys = dxy >> 4;
     const int dyxs = dyx >> 4;
     const int dyys = dyy2 >> 4;
-    const uint16_t r4[4]   = { r, r, r, r };
-    const uint16_t dxy4[4] = { dxys, dxys, dxys, dxys };
-    const uint16_t dyy4[4] = { dyys, dyys, dyys, dyys };
-    const uint64_t shift2  = 2 * shift;
     uint8_t edge_buf[(MAX_H + 1) * EDGE_EMU_STRIDE];
-    int x, y;
 
     const int dxw = dxx2 * (w - 1);
     const int dyh = dyy2 * (h - 1);
@@ -74,6 +80,7 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
     }
 
     src += ix + iy * stride;
+    const ptrdiff_t dst_stride = stride;
     ptrdiff_t src_stride = stride;
     if (need_emu) {
         ff_emulated_edge_mc_8(edge_buf, src, EDGE_EMU_STRIDE, src_stride, w + 
1, h + 1, ix, iy, width, height);
@@ -81,88 +88,138 @@ static void gmc_mmx(uint8_t *dst, const uint8_t *src,
         src_stride = EDGE_EMU_STRIDE;
     }
 
+#if ARCH_X86_32
+    xmm_u16 dxy8, dyy8, r8;
+    DECLARE_ALIGNED_16(uint64_t, shift2) = 2 * shift;
+#endif
+
     __asm__ volatile (
-        "movd         %0, %%mm6         \n\t"
-        "pxor      %%mm7, %%mm7         \n\t"
-        "punpcklwd %%mm6, %%mm6         \n\t"
-        "punpcklwd %%mm6, %%mm6         \n\t"
-        :: "r" (1 << shift));
+        "movd             %[dxxs], %%xmm2     \n\t"
+        "movd             %[dyxs], %%xmm3     \n\t"
+        "movd              %[oxs], %%xmm1     \n\t"
+        SPLATW(xmm2)
+        "movd              %[oys], %%xmm7     \n\t"
+        SPLATW(xmm3)
+        "pmullw "MANGLE(pw_0to7)", %%xmm2     \n\t"
+        SPLATW(xmm1)
+        "movd                %[s], %%xmm6     \n\t"
+        "pmullw "MANGLE(pw_0to7)", %%xmm3     \n\t"
+        "movq            (%[src]), %%xmm5     \n\t"
+        SPLATW(xmm7)
+#if ARCH_X86_32
+        "movd             %[dxys], %%xmm0     \n\t"
+#else
+        "movd             %[dxys], %%xmm11    \n\t"
+#endif
+        "paddw             %%xmm2, %%xmm1     \n\t"
+        "movq           1(%[src]), %%xmm2     \n\t"
+        SPLATW(xmm6)
+#if ARCH_X86_32
+        "movd             %[dyys], %%xmm4     \n\t"
+#else
+        "movd             %[dyys], %%xmm9     \n\t"
+#endif
+        "paddw             %%xmm3, %%xmm7     \n\t"
+        "punpcklbw         %%xmm2, %%xmm5     \n\t"
+#if ARCH_X86_32
+        SPLATW(xmm0)
+        "movd                %[r], %%xmm2     \n\t"
+        SPLATW(xmm4)
+        "movdqa            %%xmm0, %[dxy8]    \n\t"
+        SPLATW(xmm2)
+        "movdqa            %%xmm4, %[dyy8]    \n\t"
+        "movdqa            %%xmm2, %[r8]      \n\t"
+#else
+        SPLATW(xmm11)
+        "movd                %[r], %%xmm8     \n\t"
+        SPLATW(xmm9)
+        SPLATW(xmm8)
+        "movd           %[shift2], %%xmm12    \n\t"
+#endif
 
-    for (x = 0; x < w; x += 4) {
-        uint16_t dx4[4] = { oxs - dxys + dxxs * (x + 0),
-                            oxs - dxys + dxxs * (x + 1),
-                            oxs - dxys + dxxs * (x + 2),
-                            oxs - dxys + dxxs * (x + 3) };
-        uint16_t dy4[4] = { oys - dyys + dyxs * (x + 0),
-                            oys - dyys + dyxs * (x + 1),
-                            oys - dyys + dyxs * (x + 2),
-                            oys - dyys + dyxs * (x + 3) };
+        "1:                                   \n\t"
+        "add        %[src_stride], %[src]     \n\t"
+        "movq            (%[src]), %%xmm3     \n\t"
+        "movq           1(%[src]), %%xmm0     \n\t"
+        "movdqa            %%xmm1, %%xmm4     \n\t"
+        "psrlw                $12, %%xmm4     \n\t" // dx
+        "movdqa            %%xmm6, %%xmm2     \n\t"
+        "psubw             %%xmm4, %%xmm2     \n\t" // (s-dx)
+        "psllw                 $8, %%xmm4     \n\t"
+        "por               %%xmm4, %%xmm2     \n\t" // s-dx,dx,s-dx,dx (bytes)
+        "pmaddubsw         %%xmm2, %%xmm5     \n\t" // src[0, 0] * (s - dx) + 
src[1,0] * dx
+        "punpcklbw         %%xmm0, %%xmm3     \n\t"
+        "movdqa            %%xmm3, %%xmm0     \n\t"
+        "pmaddubsw         %%xmm2, %%xmm3     \n\t" // src[0, 1] * (s - dx) + 
src[1,1] * dx
+#if ARCH_X86_32
+        "paddw            %[dxy8], %%xmm1     \n\t"
+#else
+        "paddw            %%xmm11, %%xmm1     \n\t"
+#endif
+        "movdqa            %%xmm7, %%xmm4     \n\t"
+        "movdqa            %%xmm6, %%xmm2     \n\t"
+        "psrlw                $12, %%xmm4     \n\t" // dy
+        "psubw             %%xmm4, %%xmm2     \n\t" // (s-dy)
+        "pmullw            %%xmm5, %%xmm2     \n\t" // (src[0, 0] * (s - dx) + 
src[1,0] * dx) * (s - dy)
+#if ARCH_X86_32
+        "paddw            %[dyy8], %%xmm7     \n\t"
+#else
+        "paddw             %%xmm9, %%xmm7     \n\t"
+#endif
+        "pmullw            %%xmm3, %%xmm4     \n\t" // (src[0, 1] * (s - dx) + 
src[1,1] * dx) * dy
 
-        for (y = 0; y < h; y++) {
-            __asm__ volatile (
-                "movq      %0, %%mm4    \n\t"
-                "movq      %1, %%mm5    \n\t"
-                "paddw     %2, %%mm4    \n\t"
-                "paddw     %3, %%mm5    \n\t"
-                "movq   %%mm4, %0       \n\t"
-                "movq   %%mm5, %1       \n\t"
-                "psrlw    $12, %%mm4    \n\t"
-                "psrlw    $12, %%mm5    \n\t"
-                : "+m" (*dx4), "+m" (*dy4)
-                : "m" (*dxy4), "m" (*dyy4));
+#if ARCH_X86_32
+        "paddw              %[r8], %%xmm2     \n\t"
+#else
+        "paddw             %%xmm8, %%xmm2     \n\t"
+#endif
+        "paddw             %%xmm2, %%xmm4     \n\t"
 
-            __asm__ volatile (
-                "movq      %%mm6, %%mm2 \n\t"
-                "movq      %%mm6, %%mm1 \n\t"
-                "psubw     %%mm4, %%mm2 \n\t"
-                "psubw     %%mm5, %%mm1 \n\t"
-                "movq      %%mm2, %%mm0 \n\t"
-                "movq      %%mm4, %%mm3 \n\t"
-                "pmullw    %%mm1, %%mm0 \n\t" // (s - dx) * (s - dy)
-                "pmullw    %%mm5, %%mm3 \n\t" // dx * dy
-                "pmullw    %%mm5, %%mm2 \n\t" // (s - dx) * dy
-                "pmullw    %%mm4, %%mm1 \n\t" // dx * (s - dy)
+#if ARCH_X86_32
+        "psrlw          %[shift2], %%xmm4     \n\t"
+#else
+        "psrlw            %%xmm12, %%xmm4     \n\t"
+#endif
+        "packuswb          %%xmm4, %%xmm4     \n\t"
+        "movq              %%xmm4, (%[dst])   \n\t"
+        "movdqa            %%xmm0, %%xmm5     \n\t"
+        "add        %[dst_stride], %[dst]     \n\t"
 
-                "movd         %4, %%mm5 \n\t"
-                "movd         %3, %%mm4 \n\t"
-                "punpcklbw %%mm7, %%mm5 \n\t"
-                "punpcklbw %%mm7, %%mm4 \n\t"
-                "pmullw    %%mm5, %%mm3 \n\t" // src[1, 1] * dx * dy
-                "pmullw    %%mm4, %%mm2 \n\t" // src[0, 1] * (s - dx) * dy
-
-                "movd         %2, %%mm5 \n\t"
-                "movd         %1, %%mm4 \n\t"
-                "punpcklbw %%mm7, %%mm5 \n\t"
-                "punpcklbw %%mm7, %%mm4 \n\t"
-                "pmullw    %%mm5, %%mm1 \n\t" // src[1, 0] * dx * (s - dy)
-                "pmullw    %%mm4, %%mm0 \n\t" // src[0, 0] * (s - dx) * (s - 
dy)
-                "paddw        %5, %%mm1 \n\t"
-                "paddw     %%mm3, %%mm2 \n\t"
-                "paddw     %%mm1, %%mm0 \n\t"
-                "paddw     %%mm2, %%mm0 \n\t"
-
-                "psrlw        %6, %%mm0 \n\t"
-                "packuswb  %%mm0, %%mm0 \n\t"
-                "movd      %%mm0, %0    \n\t"
-
-                : "=m" (dst[x + y * stride])
-                : "m" (src[0]), "m" (src[1]),
-                  "m" (src[src_stride]), "m" (src[src_stride + 1]),
-                  "m" (*r4), "m" (shift2));
-            src += src_stride;
-        }
-        src += 4 - h * src_stride;
-    }
+        "decl                %[h]             \n\t"
+        "jnz                   1b             \n\t"
+        : [dst]"+r"(dst), [src]"+r"(src),
+#if HAVE_6REGS || HAVE_INLINE_ASM_DIRECT_SYMBOL_REFS
+        [h]"+r"(h)
+#else
+        [h]"+m"(h)
+#endif
+#if ARCH_X86_32
+          , [dxy8]"=m" (dxy8), [dyy8]"=m" (dyy8), [r8]"=m" (r8)
+#endif
+        : [dst_stride]"r"(dst_stride), [src_stride]"r"(src_stride),
+          [s]"g" (1 << shift),
+#if ARCH_X86_32
+          [shift2]"m" (shift2),
+#else
+          [shift2]"g" (2*shift),
+#endif
+          [oxs]"g"(oxs),  [oys]"g"(oys),  [dxxs]"g"(dxxs), [dyxs]"g"(dyxs),
+          [dxys]"g"(dxys), [dyys]"g"(dyys), [r]"g"(r) 
NAMED_CONSTRAINTS_ADD(pw_0to7)
+        : XMM_CLOBBERS("xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", 
"xmm7",)
+#if ARCH_X86_64
+          XMM_CLOBBERS("xmm8", "xmm9", "xmm10", "xmm11", "xmm12",)
+#endif
+         "memory");
 }
 
-#endif /* HAVE_INLINE_ASM */
+#endif /* HAVE_SSSE3_INLINE */
 
 av_cold void ff_mpeg4videodsp_init_x86(Mpeg4VideoDSPContext *c)
 {
-#if HAVE_INLINE_ASM
+#if HAVE_SSSE3_INLINE
     int cpu_flags = av_get_cpu_flags();
 
-    if (INLINE_MMX(cpu_flags))
-        c->gmc = gmc_mmx;
-#endif /* HAVE_INLINE_ASM */
+    if (INLINE_SSSE3(cpu_flags))
+        c->gmc = gmc_ssse3;
+#endif /* HAVE_SSSE3_INLINE */
 }
diff --git a/tests/checkasm/mpeg4videodsp.c b/tests/checkasm/mpeg4videodsp.c
index 79a3ac5805..7c7e82c4b0 100644
--- a/tests/checkasm/mpeg4videodsp.c
+++ b/tests/checkasm/mpeg4videodsp.c
@@ -79,7 +79,7 @@ static void checkasm_check_gmc(const Mpeg4VideoDSPContext 
*const mdsp)
     DECLARE_ALIGNED_8(uint8_t, buf_ref)[MAX_BLOCK_HEIGHT * MAX_STRIDE];
     DECLARE_ALIGNED_4(uint8_t, srcbuf)[MAX_STRIDE * MAX_HEIGHT];
 
-    declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *dst, const uint8_t *src,
+    declare_func(void, uint8_t *dst, const uint8_t *src,
                  int stride, int h, int ox, int oy,
                  int dxx, int dxy, int dyx, int dyy,
                  int shift, int r, int width, int height);
-- 
2.52.0


>From 9a9ba467aaeb973c21d999fcab0755511a0e992c Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <[email protected]>
Date: Sat, 14 Mar 2026 23:28:45 +0100
Subject: [PATCH 5/5] avcodec/x86/mpeg4videodsp: Use SSE2 emulated_edge_mc

Possible now that this function is no longer MMX.

Old benchmarks:
gmc_edge_emulation_c:                                  782.3 ( 1.00x)
gmc_edge_emulation_ssse3:                              220.3 ( 3.55x)

New benchmarks:
gmc_edge_emulation_c:                                  770.9 ( 1.00x)
gmc_edge_emulation_ssse3:                              111.0 ( 6.94x)

Signed-off-by: Andreas Rheinhardt <[email protected]>
---
 libavcodec/videodsp.c          |  4 ++--
 libavcodec/videodsp.h          |  8 --------
 libavcodec/videodsp_template.c | 15 ++++++---------
 libavcodec/x86/mpeg4videodsp.c |  5 +++--
 libavcodec/x86/videodsp.h      | 32 ++++++++++++++++++++++++++++++++
 libavcodec/x86/videodsp_init.c | 15 ++++++++-------
 6 files changed, 51 insertions(+), 28 deletions(-)
 create mode 100644 libavcodec/x86/videodsp.h

diff --git a/libavcodec/videodsp.c b/libavcodec/videodsp.c
index c66757ce83..230a1bfb74 100644
--- a/libavcodec/videodsp.c
+++ b/libavcodec/videodsp.c
@@ -40,9 +40,9 @@ av_cold void ff_videodsp_init(VideoDSPContext *ctx, int bpc)
 {
     ctx->prefetch = just_return;
     if (bpc <= 8) {
-        ctx->emulated_edge_mc = ff_emulated_edge_mc_8;
+        ctx->emulated_edge_mc = emulated_edge_mc_8;
     } else {
-        ctx->emulated_edge_mc = ff_emulated_edge_mc_16;
+        ctx->emulated_edge_mc = emulated_edge_mc_16;
     }
 
 #if ARCH_AARCH64
diff --git a/libavcodec/videodsp.h b/libavcodec/videodsp.h
index 1be3188d09..4f081e0869 100644
--- a/libavcodec/videodsp.h
+++ b/libavcodec/videodsp.h
@@ -29,14 +29,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#define EMULATED_EDGE(depth) \
-void ff_emulated_edge_mc_ ## depth(uint8_t *dst, const uint8_t *src, \
-                                   ptrdiff_t dst_stride, ptrdiff_t src_stride, 
\
-                                   int block_w, int block_h,\
-                                   int src_x, int src_y, int w, int h);
-
-EMULATED_EDGE(8)
-
 typedef struct VideoDSPContext {
     /**
      * Copy a rectangular area of samples to a temporary buffer and replicate
diff --git a/libavcodec/videodsp_template.c b/libavcodec/videodsp_template.c
index d653f4d524..2475366157 100644
--- a/libavcodec/videodsp_template.c
+++ b/libavcodec/videodsp_template.c
@@ -20,15 +20,12 @@
  */
 
 #include "bit_depth_template.c"
-#if BIT_DEPTH != 8
-// ff_emulated_edge_mc_8 is used by the x86 MpegVideoDSP API.
-static
-#endif
-void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
-                               ptrdiff_t buf_linesize,
-                               ptrdiff_t src_linesize,
-                               int block_w, int block_h,
-                               int src_x, int src_y, int w, int h)
+
+static void FUNC(emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
+                                   ptrdiff_t buf_linesize,
+                                   ptrdiff_t src_linesize,
+                                   int block_w, int block_h,
+                                   int src_x, int src_y, int w, int h)
 {
     int x, y;
     int start_y, start_x, end_y, end_x;
diff --git a/libavcodec/x86/mpeg4videodsp.c b/libavcodec/x86/mpeg4videodsp.c
index f3f7036157..47fd413da7 100644
--- a/libavcodec/x86/mpeg4videodsp.c
+++ b/libavcodec/x86/mpeg4videodsp.c
@@ -23,7 +23,7 @@
 #include "libavutil/x86/asm.h"
 #include "libavutil/x86/cpu.h"
 #include "libavcodec/mpeg4videodsp.h"
-#include "libavcodec/videodsp.h"
+#include "videodsp.h"
 
 #if HAVE_SSSE3_INLINE
 
@@ -83,7 +83,8 @@ static void gmc_ssse3(uint8_t *dst, const uint8_t *src,
     const ptrdiff_t dst_stride = stride;
     ptrdiff_t src_stride = stride;
     if (need_emu) {
-        ff_emulated_edge_mc_8(edge_buf, src, EDGE_EMU_STRIDE, src_stride, w + 
1, h + 1, ix, iy, width, height);
+        ff_emulated_edge_mc_sse2(edge_buf, src, EDGE_EMU_STRIDE, src_stride,
+                                 w + 1, h + 1, ix, iy, width, height);
         src        = edge_buf;
         src_stride = EDGE_EMU_STRIDE;
     }
diff --git a/libavcodec/x86/videodsp.h b/libavcodec/x86/videodsp.h
new file mode 100644
index 0000000000..5d58108c98
--- /dev/null
+++ b/libavcodec/x86/videodsp.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_X86_VIDEODSP_H
+#define AVCODEC_X86_VIDEODSP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+void ff_emulated_edge_mc_sse2(uint8_t *buf, const uint8_t *src,
+                              ptrdiff_t buf_stride,
+                              ptrdiff_t src_stride,
+                              int block_w, int block_h,
+                              int src_x, int src_y, int w,
+                              int h);
+
+#endif /* AVCODEC_X86_VIDEODSP_H */
diff --git a/libavcodec/x86/videodsp_init.c b/libavcodec/x86/videodsp_init.c
index 7f3c837227..e40482e1d0 100644
--- a/libavcodec/x86/videodsp_init.c
+++ b/libavcodec/x86/videodsp_init.c
@@ -27,6 +27,7 @@
 #include "libavutil/x86/asm.h"
 #include "libavutil/x86/cpu.h"
 #include "libavcodec/videodsp.h"
+#include "videodsp.h"
 
 typedef void emu_edge_vfix_func(uint8_t *dst, x86_reg dst_stride,
                                 const uint8_t *src, x86_reg src_stride,
@@ -187,12 +188,12 @@ static av_always_inline void emulated_edge_mc(uint8_t 
*dst, const uint8_t *src,
     }
 }
 
-static av_noinline void emulated_edge_mc_sse2(uint8_t *buf, const uint8_t *src,
-                                              ptrdiff_t buf_stride,
-                                              ptrdiff_t src_stride,
-                                              int block_w, int block_h,
-                                              int src_x, int src_y, int w,
-                                              int h)
+void ff_emulated_edge_mc_sse2(uint8_t *buf, const uint8_t *src,
+                              ptrdiff_t buf_stride,
+                              ptrdiff_t src_stride,
+                              int block_w, int block_h,
+                              int src_x, int src_y, int w,
+                              int h)
 {
     emulated_edge_mc(buf, src, buf_stride, src_stride, block_w, block_h,
                      src_x, src_y, w, h, vfixtbl_sse2, &ff_emu_edge_vvar_sse,
@@ -223,7 +224,7 @@ av_cold void ff_videodsp_init_x86(VideoDSPContext *ctx, int 
bpc)
         ctx->prefetch = ff_prefetch_mmxext;
     }
     if (EXTERNAL_SSE2(cpu_flags) && bpc <= 8) {
-        ctx->emulated_edge_mc = emulated_edge_mc_sse2;
+        ctx->emulated_edge_mc = ff_emulated_edge_mc_sse2;
     }
 #if HAVE_AVX2_EXTERNAL
     if (EXTERNAL_AVX2(cpu_flags) && bpc <= 8) {
-- 
2.52.0

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

Reply via email to