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

Add LSX optimizations for swscale/loongarch. 

1. Add LSX fast bilinear horizontal scale optimitzaitons.
2. Add LSX fast path for NV12/NV21 to 32-bit RGB conversons.


>From a64b9bcbe8b798a8d15ae6c5745be21bf3e019a8 Mon Sep 17 00:00:00 2001
From: jinbo <[email protected]>
Date: Mon, 31 Aug 2026 17:38:20 +0800
Subject: [PATCH 1/2] swscale/loongarch: add unscaled NV12/NV21 to 32-bit RGB
 conversions

Add fast path for NV12/NV21 to RGBA/ARGB/BGRA/ABGR conversions. The
LSX implementations are based on the unscaled YUV420P to RGBA/ARGB/
BGRA/ABGR conversions. The difference is that the NV12/NV21 load UV
data from src[1],  and then use `vshuf` to prepare the U and V data.
The C reference implementations work as the runtime fallback when
LSX is unavailable, and are mainly used by the checkasm yuv2rgb test
to verify the LSX path. The LSX implementations use the fixed-point
coefficient math, while the C references use the lookup tables like
yuv2rgb_c_32, both differ by at most a couple of LSBs.

checkasm --bench on 3A5000 4 cores 2.5GHz:
nv21_bgra_8_c:                14.5
nv21_bgra_8_lsx:               7.6 ( 1.88x)
nv21_bgra_128_c:             220.1
nv21_bgra_128_lsx:            35.6 ( 6.17x)
nv21_bgra_1080_c:           1819.7
nv21_bgra_1080_lsx:          304.0 ( 5.98x)
nv21_bgra_1920_c:           3236.6
nv21_bgra_1920_lsx:          510.8 ( 6.33x)

Performance with:
$ ./ffmpeg -cpuflags lsx -f lavfi -i 
"smptebars=size=3840x2160:rate=30:duration=100,format=nv12" \
-threads 1 -vf "format=bgra" -f null -
before: 14fps
after : 114fps
---
 libswscale/loongarch/Makefile            |   1 +
 libswscale/loongarch/swscale_loongarch.h |  22 ++
 libswscale/loongarch/swscale_unscaled.c  | 108 ++++++++++
 libswscale/loongarch/yuv2rgb_lsx.c       | 264 +++++++++++++++++++++++
 libswscale/swscale_internal.h            |   1 +
 libswscale/swscale_unscaled.c            |   2 +
 6 files changed, 398 insertions(+)
 create mode 100644 libswscale/loongarch/swscale_unscaled.c

diff --git a/libswscale/loongarch/Makefile b/libswscale/loongarch/Makefile
index 7ba11d492e..06aed9d245 100644
--- a/libswscale/loongarch/Makefile
+++ b/libswscale/loongarch/Makefile
@@ -5,6 +5,7 @@ LASX-OBJS-$(CONFIG_SWSCALE) += loongarch/swscale_lasx.o \
                                loongarch/rgb2rgb_lasx.o \
                                loongarch/output_lasx.o
 LSX-OBJS-$(CONFIG_SWSCALE)  += loongarch/swscale.o \
+                               loongarch/swscale_unscaled.o \
                                loongarch/swscale_lsx.o \
                                loongarch/input.o   \
                                loongarch/output.o  \
diff --git a/libswscale/loongarch/swscale_loongarch.h 
b/libswscale/loongarch/swscale_loongarch.h
index cff3964c22..a8297b6972 100644
--- a/libswscale/loongarch/swscale_loongarch.h
+++ b/libswscale/loongarch/swscale_loongarch.h
@@ -208,4 +208,26 @@ av_cold void ff_sws_init_output_lasx(SwsInternal *c,
                                      yuv2anyX_fn *yuv2anyX);
 #endif // #if HAVE_LASX
 
+int yuv420_nv12_bgra32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv21_bgra32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv12_rgba32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv21_rgba32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv12_argb32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv21_argb32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv12_abgr32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+int yuv420_nv21_abgr32_lsx(SwsInternal *c, const uint8_t *const src[], const 
int srcStride[],
+                           int srcSliceY, int srcSliceH, uint8_t *const dst[], 
const int dstStride[]);
+
+int ff_nv12ToRgb32_c(SwsInternal *c, const uint8_t *const src[], const int 
srcStride[],
+                     int srcSliceY, int srcSliceH, uint8_t *const dst[], const 
int dstStride[]);
+int ff_nv21ToRgb32_c(SwsInternal *c, const uint8_t *const src[], const int 
srcStride[],
+                     int srcSliceY, int srcSliceH, uint8_t *const dst[], const 
int dstStride[]);
+
 #endif /* SWSCALE_LOONGARCH_SWSCALE_LOONGARCH_H */
diff --git a/libswscale/loongarch/swscale_unscaled.c 
b/libswscale/loongarch/swscale_unscaled.c
new file mode 100644
index 0000000000..1111e6895d
--- /dev/null
+++ b/libswscale/loongarch/swscale_unscaled.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2026 Loongson Technology Co. Ltd.
+ * Contributed by Bo Jin([email protected])
+ *
+ * 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
+ */
+
+#include "swscale_loongarch.h"
+#include "libswscale/swscale_internal.h"
+#include "libavutil/loongarch/cpu.h"
+
+/* C reference implementations of the unscaled NV12/NV21 -> 32-bit RGB
+ * conversions (interleaved chroma consumed from src[1]). They work as
+ * the runtime fallback on LoongArch when LSX is unavailable, so they use
+ * the lookup-table math (like yuv2rgb_c_32) rather than the fixed-point
+ * coefficient form; the LSX implementations differ from them by at most
+ * a couple of LSBs, which the checkasm yuv2rgb test tolerates. */
+#define NVXXRGB32FUNC(func_name, uv_swap)                                   \
+int func_name(SwsInternal *c, const uint8_t *const src[],                   \
+              const int srcStride[], int srcSliceY, int srcSliceH,          \
+              uint8_t *const dst[], const int dstStride[])                  \
+{                                                                           \
+    int y;                                                                  \
+                                                                            \
+    for (y = 0; y < srcSliceH; y++) {                                       \
+        int yd = y + srcSliceY;                                             \
+        uint32_t *dest = (uint32_t *)(dst[0] + yd * dstStride[0]);          \
+        const uint8_t *py  = src[0] +      y      * srcStride[0];           \
+        const uint8_t *puv = src[1] + (y >> 1) * srcStride[1];              \
+        int i;                                                              \
+                                                                            \
+        for (i = 0; i < c->opts.dst_w; i++) {                               \
+            int Y = py[i];                                                  \
+            int U = puv[(i >> 1) * 2 + uv_swap];                            \
+            int V = puv[(i >> 1) * 2 + (uv_swap ^ 1)];                      \
+            uint32_t *r = (void *)c->table_rV[V+YUVRGB_TABLE_HEADROOM];     \
+            uint32_t *g = (void *)(c->table_gU[U+YUVRGB_TABLE_HEADROOM]     \
+                                 + c->table_gV[V+YUVRGB_TABLE_HEADROOM]);   \
+            uint32_t *b = (void *)c->table_bU[U+YUVRGB_TABLE_HEADROOM];     \
+            dest[i] = r[Y] + g[Y] + b[Y];                                   \
+        }                                                                   \
+    }                                                                       \
+    return srcSliceH;                                                       \
+}
+
+NVXXRGB32FUNC(ff_nv12ToRgb32_c, 0)
+NVXXRGB32FUNC(ff_nv21ToRgb32_c, 1)
+
+/* Unscaled NV12/NV21 -> packed 32-bit RGB */
+void ff_get_unscaled_swscale_loongarch(SwsInternal *c)
+{
+    int cpu_flags = av_get_cpu_flags();
+    int use_lsx = have_lsx(cpu_flags);
+
+    if ((c->opts.dst_h & 1) || (c->opts.flags & SWS_ACCURATE_RND))
+        return;
+
+    if (c->opts.src_format != AV_PIX_FMT_NV12 &&
+        c->opts.src_format != AV_PIX_FMT_NV21)
+        return;
+
+    switch (c->opts.dst_format) {
+    case AV_PIX_FMT_RGBA:
+        c->convert_unscaled = use_lsx ?
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? yuv420_nv12_rgba32_lsx
+                                                   : yuv420_nv21_rgba32_lsx) :
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? ff_nv12ToRgb32_c
+                                                   : ff_nv21ToRgb32_c);
+        break;
+    case AV_PIX_FMT_ARGB:
+        c->convert_unscaled = use_lsx ?
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? yuv420_nv12_argb32_lsx
+                                                   : yuv420_nv21_argb32_lsx) :
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? ff_nv12ToRgb32_c
+                                                   : ff_nv21ToRgb32_c);
+        break;
+    case AV_PIX_FMT_BGRA:
+        c->convert_unscaled = use_lsx ?
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? yuv420_nv12_bgra32_lsx
+                                                   : yuv420_nv21_bgra32_lsx) :
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? ff_nv12ToRgb32_c
+                                                   : ff_nv21ToRgb32_c);
+        break;
+    case AV_PIX_FMT_ABGR:
+        c->convert_unscaled = use_lsx ?
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? yuv420_nv12_abgr32_lsx
+                                                   : yuv420_nv21_abgr32_lsx) :
+            (c->opts.src_format == AV_PIX_FMT_NV12 ? ff_nv12ToRgb32_c
+                                                   : ff_nv21ToRgb32_c);
+        break;
+    default:
+        break;
+    }
+}
diff --git a/libswscale/loongarch/yuv2rgb_lsx.c 
b/libswscale/loongarch/yuv2rgb_lsx.c
index 919fd00234..df9d4de261 100644
--- a/libswscale/loongarch/yuv2rgb_lsx.c
+++ b/libswscale/loongarch/yuv2rgb_lsx.c
@@ -46,6 +46,17 @@
     DUP2_ARG2(__lsx_vilvh_b, zero, m_y1, zero, m_y2, m_y1_h, m_y2_h); \
     DUP2_ARG2(__lsx_vilvl_b, zero, m_y1, zero, m_y2, m_y1, m_y2);     \
 
+#define LOAD_YUV_16_NV12                                              \
+    m_y1 = __lsx_vld(py_1, 0);                                        \
+    m_y2 = __lsx_vld(py_2, 0);                                        \
+    m_uv = __lsx_vld(puv, 0);                                         \
+    m_u   = __lsx_vshuf_b(zero, m_uv, shuf_u);                        \
+    m_u_h = __lsx_vshuf_b(zero, m_uv, shuf_uh);                       \
+    m_v   = __lsx_vshuf_b(zero, m_uv, shuf_v);                        \
+    m_v_h = __lsx_vshuf_b(zero, m_uv, shuf_vh);                       \
+    DUP2_ARG2(__lsx_vilvh_b, zero, m_y1, zero, m_y2, m_y1_h, m_y2_h); \
+    DUP2_ARG2(__lsx_vilvl_b, zero, m_y1, zero, m_y2, m_y1, m_y2);     \
+
 /* YUV2RGB method
  * The conversion method is as follows:
  * R = Y' * y_coeff + V' * vr_coeff
@@ -252,6 +263,83 @@
     return srcSliceH;                       \
 }
 
+#define SHUF_UV_EVEN    {0x1002100210001000, 0x1006100610041004}
+#define SHUF_UV_EVEN_HI {0x100a100a10081008, 0x100e100e100c100c}
+#define SHUF_UV_ODD     {0x1003100310011001, 0x1007100710051005}
+#define SHUF_UV_ODD_HI  {0x100b100b10091009, 0x100f100f100d100d}
+
+#define YUV2RGBFUNC32_NV12(func_name, dst_type, alpha,                         
    \
+                           SHUF_U, SHUF_UH, SHUF_V, SHUF_VH)                   
    \
+           int func_name(SwsInternal *c, const uint8_t *const src[],           
    \
+                         const int srcStride[], int srcSliceY, int srcSliceH,  
    \
+                         uint8_t *const dst[], const int dstStride[])          
    \
+{                                                                              
     \
+    int x, y, h_size, vshift, res;                                             
     \
+    __m128i m_y1, m_y2, m_u, m_v, m_uv;                                        
     \
+    __m128i m_y1_h, m_y2_h, m_u_h, m_v_h;                                      
     \
+    __m128i y_1, y_2, u2g, v2g, u2b, v2r, rgb1_l, rgb1_h;                      
     \
+    __m128i rgb2_l, rgb2_h, r1, g1, b1, r2, g2, b2;                            
     \
+    __m128i a = __lsx_vldi(0xFF);                                              
     \
+    __m128i zero = __lsx_vldi(0);                                              
     \
+    __m128i shuf_u  = SHUF_U;                                                  
     \
+    __m128i shuf_uh = SHUF_UH;                                                 
     \
+    __m128i shuf_v  = SHUF_V;                                                  
     \
+    __m128i shuf_vh = SHUF_VH;                                                 
     \
+                                                                               
     \
+    YUV2RGB_LOAD_COE                                                           
     \
+                                                                               
     \
+    h_size = c->opts.dst_w >> 4;                                               
     \
+    res = (c->opts.dst_w & 15) >> 1;                                           
     \
+    vshift = c->opts.src_format != AV_PIX_FMT_YUV422P;                         
     \
+    for (y = 0; y < srcSliceH; y += 2) {                                       
     \
+        int yd = y + srcSliceY;                                                
     \
+        dst_type av_unused *r, *g, *b;                                         
     \
+        dst_type *image1    = (dst_type *)(dst[0] + (yd)     * dstStride[0]);  
     \
+        dst_type *image2    = (dst_type *)(dst[0] + (yd + 1) * dstStride[0]);  
     \
+        const uint8_t *py_1 = src[0] +               y * srcStride[0];         
     \
+        const uint8_t *py_2 = py_1   +                   srcStride[0];         
     \
+        const uint8_t *puv  = src[1] +   (y >> vshift) * srcStride[1];         
     \
+        for(x = 0; x < h_size; x++) {                                          
     \
+
+#define DEALYUV2RGBREMAIN32_NV12                                               
     \
+            py_1 += 16;                                                        
     \
+            py_2 += 16;                                                        
     \
+            puv += 16;                                                         
     \
+            image1 += 16;                                                      
     \
+            image2 += 16;                                                      
     \
+        }                                                                      
     \
+        for (x = 0; x < res; x++) {                                            
     \
+            av_unused int U, V, Y;                                             
     \
+            U = puv[0];                                                        
     \
+            V = puv[1];                                                        
     \
+            r = (void *)c->table_rV[V+YUVRGB_TABLE_HEADROOM];                  
     \
+            g = (void *)(c->table_gU[U+YUVRGB_TABLE_HEADROOM]                  
     \
+                       + c->table_gV[V+YUVRGB_TABLE_HEADROOM]);                
     \
+            b = (void *)c->table_bU[U+YUVRGB_TABLE_HEADROOM];                  
     \
+
+#define DEALYUV2RGBREMAIN32_NV21                                               
     \
+            py_1 += 16;                                                        
     \
+            py_2 += 16;                                                        
     \
+            puv += 16;                                                         
     \
+            image1 += 16;                                                      
     \
+            image2 += 16;                                                      
     \
+        }                                                                      
     \
+        for (x = 0; x < res; x++) {                                            
     \
+            av_unused int U, V, Y;                                             
     \
+            U = puv[1];                                                        
     \
+            V = puv[0];                                                        
     \
+            r = (void *)c->table_rV[V+YUVRGB_TABLE_HEADROOM];                  
     \
+            g = (void *)(c->table_gU[U+YUVRGB_TABLE_HEADROOM]                  
     \
+                       + c->table_gV[V+YUVRGB_TABLE_HEADROOM]);                
     \
+            b = (void *)c->table_bU[U+YUVRGB_TABLE_HEADROOM];                  
     \
+
+#define ENDRES32_NV12                       \
+    puv += 2;                               \
+    py_1 += 2;                              \
+    py_2 += 2;                              \
+    image1 += 2;                            \
+    image2 += 2;                            \
+
 YUV2RGBFUNC(yuv420_rgb24_lsx, uint8_t, 0)
     LOAD_YUV_16
     YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
@@ -359,3 +447,179 @@ YUV2RGBFUNC32(yuv420_abgr32_lsx, uint32_t, 0)
     PUTRGB(image2, py_2);
     ENDRES32
     END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv12_bgra32_lsx, uint32_t, 0,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(b1, g1, r1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(b2, g2, r2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(b1, g1, r1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(b2, g2, r2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV12
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv21_bgra32_lsx, uint32_t, 0,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(b1, g1, r1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(b2, g2, r2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(b1, g1, r1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(b2, g2, r2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV21
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv12_rgba32_lsx, uint32_t, 0,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(r1, g1, b1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(r2, g2, b2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(r1, g1, b1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(r2, g2, b2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV12
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv21_rgba32_lsx, uint32_t, 0,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(r1, g1, b1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(r2, g2, b2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(r1, g1, b1, a, rgb1_l, rgb1_h);
+    RGB32_PACK(r2, g2, b2, a, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV21
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv12_argb32_lsx, uint32_t, 0,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, r1, g1, b1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, r2, g2, b2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, r1, g1, b1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, r2, g2, b2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV12
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv21_argb32_lsx, uint32_t, 0,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, r1, g1, b1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, r2, g2, b2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, r1, g1, b1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, r2, g2, b2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV21
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv12_abgr32_lsx, uint32_t, 0,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, b1, g1, r1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, b2, g2, r2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, b1, g1, r1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, b2, g2, r2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV12
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
+
+YUV2RGBFUNC32_NV12(yuv420_nv21_abgr32_lsx, uint32_t, 0,
+                   SHUF_UV_ODD,
+                   SHUF_UV_ODD_HI,
+                   SHUF_UV_EVEN,
+                   SHUF_UV_EVEN_HI)
+    LOAD_YUV_16_NV12
+    YUV2RGB(m_y1, m_y2, m_u, m_v, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, b1, g1, r1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, b2, g2, r2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1);
+    RGB32_STORE(rgb2_l, rgb2_h, image2);
+    YUV2RGB(m_y1_h, m_y2_h, m_u_h, m_v_h, r1, g1, b1, r2, g2, b2);
+    RGB32_PACK(a, b1, g1, r1, rgb1_l, rgb1_h);
+    RGB32_PACK(a, b2, g2, r2, rgb2_l, rgb2_h);
+    RGB32_STORE(rgb1_l, rgb1_h, image1 + 8);
+    RGB32_STORE(rgb2_l, rgb2_h, image2 + 8);
+    DEALYUV2RGBREMAIN32_NV21
+    PUTRGB(image1, py_1);
+    PUTRGB(image2, py_2);
+    ENDRES32_NV12
+    END_FUNC()
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index a1fa50dad0..ac75cff04e 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -1013,6 +1013,7 @@ void ff_get_unscaled_swscale(SwsInternal *c);
 void ff_get_unscaled_swscale_ppc(SwsInternal *c);
 void ff_get_unscaled_swscale_arm(SwsInternal *c);
 void ff_get_unscaled_swscale_aarch64(SwsInternal *c);
+void ff_get_unscaled_swscale_loongarch(SwsInternal *c);
 
 void ff_sws_init_scale(SwsInternal *c);
 
diff --git a/libswscale/swscale_unscaled.c b/libswscale/swscale_unscaled.c
index 7acab95e03..8024db5613 100644
--- a/libswscale/swscale_unscaled.c
+++ b/libswscale/swscale_unscaled.c
@@ -2702,6 +2702,8 @@ void ff_get_unscaled_swscale(SwsInternal *c)
     ff_get_unscaled_swscale_arm(c);
 #elif ARCH_AARCH64
     ff_get_unscaled_swscale_aarch64(c);
+#elif ARCH_LOONGARCH64
+    ff_get_unscaled_swscale_loongarch(c);
 #endif
 }
 
-- 
2.52.0


>From f15fb58dcd944e870f541b266ee3c09a924b48e6 Mon Sep 17 00:00:00 2001
From: jinbo <[email protected]>
Date: Tue, 1 Sep 2026 11:28:33 +0800
Subject: [PATCH 2/2] swscale/loongarch: add LSX fast bilinear horizontal scale

Add LoongArch LSX optimized implementations of the fast bilinear
horizontal scaler (SWS_FAST_BILINEAR, hyscale_fast/hcscale_fast).

The vectorized loop processes 8 destination pixels per iteration,
computing the source offsets and interpolation weights in registers
and gathering the input samples with vshuf_b over a 32-byte window.
It covers scaling ratios down to 4x downscaling; beyond that the
scalar loop is used.

checkasm --bench on 3A5000 4 cores 2.5GHz:

hcscale_fast_c:          99.7
hcscale_fast_lsx:        28.1 ( 3.55x)
hyscale_fast_c:          51.1
hyscale_fast_lsx:        18.3 ( 2.78x)

Performance with:
$ ./ffmpeg -cpuflags lsx -f lavfi -i 
"smptebars=size=3840x2160:rate=30:duration=100,format=nv12" \
-threads 1 -vf "scale=1920:1080:sws_flags=fast_bilinear,format=bgra" -f null -
before: 36fps
after : 86fps
---
 libswscale/loongarch/Makefile                 |   1 +
 .../loongarch/hscale_fast_bilinear_lsx.c      | 162 ++++++++++++++++++
 libswscale/loongarch/swscale_init_loongarch.c |   6 +
 libswscale/loongarch/swscale_loongarch.h      |   7 +
 tests/checkasm/sw_scale.c                     | 119 +++++++++++++
 5 files changed, 295 insertions(+)
 create mode 100644 libswscale/loongarch/hscale_fast_bilinear_lsx.c

diff --git a/libswscale/loongarch/Makefile b/libswscale/loongarch/Makefile
index 06aed9d245..0b9c87100e 100644
--- a/libswscale/loongarch/Makefile
+++ b/libswscale/loongarch/Makefile
@@ -6,6 +6,7 @@ LASX-OBJS-$(CONFIG_SWSCALE) += loongarch/swscale_lasx.o \
                                loongarch/output_lasx.o
 LSX-OBJS-$(CONFIG_SWSCALE)  += loongarch/swscale.o \
                                loongarch/swscale_unscaled.o \
+                               loongarch/hscale_fast_bilinear_lsx.o \
                                loongarch/swscale_lsx.o \
                                loongarch/input.o   \
                                loongarch/output.o  \
diff --git a/libswscale/loongarch/hscale_fast_bilinear_lsx.c 
b/libswscale/loongarch/hscale_fast_bilinear_lsx.c
new file mode 100644
index 0000000000..b6097645ec
--- /dev/null
+++ b/libswscale/loongarch/hscale_fast_bilinear_lsx.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2026 Loongson Technology Co. Ltd.
+ * Contributed by Bo Jin([email protected])
+ *
+ * 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
+ */
+
+#include "swscale_loongarch.h"
+#include "libavutil/loongarch/loongson_intrinsics.h"
+
+/* Each vector iteration scales 8 destination pixels. Their source
+ * position offsets ((xpos & 0xFFFF) + j*xInc) >> 16, j in [0, 7], must
+ * stay within the 32-byte gather window, which holds while
+ * xInc <= (1 << 18); otherwise fall back to the scalar loop. */
+#define LSX_HSCALE_FAST_MAX_XINC (1 << 18)
+
+void ff_hyscale_fast_lsx(SwsInternal *c, int16_t *dst, int dstWidth,
+                         const uint8_t *src, int srcW, int xInc)
+{
+    int i = 0;
+    unsigned int xpos = 0;
+
+    if (xInc <= LSX_HSCALE_FAST_MAX_XINC) {
+        static const int32_t idx32[4]  = {0, 1, 2, 3};
+        static const int16_t idx16[8]  = {0, 1, 2, 3, 4, 5, 6, 7};
+        static const uint8_t shuf8[16] = {
+            0, 4, 8, 12, 16, 20, 24, 28,
+            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+        };
+
+        /* [0, xInc, 2*xInc, 3*xInc] as 32-bit lanes */
+        __m128i vadd_w = __lsx_vmul_w(__lsx_vreplgr2vr_w(xInc),
+                                      __lsx_vld(idx32, 0));
+        /* [0, xInc, ..., 7*xInc] modulo 2^16 as 16-bit lanes */
+        __m128i vadd16 = __lsx_vmul_h(__lsx_vreplgr2vr_h(xInc),
+                                      __lsx_vld(idx16, 0));
+        __m128i vx4    = __lsx_vreplgr2vr_w(4 * xInc);
+        __m128i vshuf8 = __lsx_vld(shuf8, 0);
+        __m128i v128   = __lsx_vreplgr2vr_h(128);
+
+        for (; i + 8 <= dstWidth && (xpos >> 16) + 32 < srcW; i += 8, xpos += 
8 * xInc) {
+            unsigned int lo = xpos & 0xFFFF;
+            unsigned int xx = xpos >> 16;
+
+            /* full 32-bit positions of the 8 pixels */
+            __m128i vc0 = __lsx_vadd_w(__lsx_vreplgr2vr_w(lo), vadd_w);
+            __m128i vc1 = __lsx_vadd_w(vc0, vx4);
+
+            /* source offsets (j = 0..7), each in [0, 15] */
+            __m128i vperm = __lsx_vshuf_b(__lsx_vsrli_w(vc1, 16),
+                                          __lsx_vsrli_w(vc0, 16), vshuf8);
+
+            /* xalpha = (xpos & 0xFFFF) >> 9, 16-bit lanes */
+            __m128i valpha = __lsx_vsrli_h(__lsx_vadd_h(__lsx_vreplgr2vr_h(lo),
+                                                        vadd16), 9);
+
+            __m128i v0 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src + xx 
+ 16, 0),
+                                                           __lsx_vld(src + xx, 
0),
+                                                           vperm), 0);
+            __m128i v1 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src + xx 
+ 17, 0),
+                                                           __lsx_vld(src + xx 
+ 1, 0),
+                                                           vperm), 0);
+
+            __m128i w0 = __lsx_vsub_h(v128, valpha);
+            __lsx_vst(__lsx_vadd_h(__lsx_vmul_h(v0, w0), __lsx_vmul_h(v1, 
valpha)),
+                      dst + i, 0);
+        }
+    }
+
+    for (; i < dstWidth; i++) {
+        unsigned int xx     = xpos >> 16;
+        unsigned int xalpha = (xpos & 0xFFFF) >> 9;
+        dst[i] = (src[xx] << 7) + (src[xx + 1] - src[xx]) * xalpha;
+        xpos  += xInc;
+    }
+    for (i = dstWidth - 1; (i * (int64_t)xInc) >> 16 >= srcW - 1; i--)
+        dst[i] = src[srcW - 1] * 128;
+}
+
+void ff_hcscale_fast_lsx(SwsInternal *c, int16_t *dst1, int16_t *dst2,
+                         int dstWidth, const uint8_t *src1,
+                         const uint8_t *src2, int srcW, int xInc)
+{
+    int i = 0;
+    unsigned int xpos = 0;
+
+    if (xInc <= LSX_HSCALE_FAST_MAX_XINC) {
+        static const int32_t idx32[4]  = {0, 1, 2, 3};
+        static const int16_t idx16[8]  = {0, 1, 2, 3, 4, 5, 6, 7};
+        static const uint8_t shuf8[16] = {
+            0, 4, 8, 12, 16, 20, 24, 28,
+            0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+        };
+
+        __m128i vadd_w = __lsx_vmul_w(__lsx_vreplgr2vr_w(xInc),
+                                      __lsx_vld(idx32, 0));
+        __m128i vadd16 = __lsx_vmul_h(__lsx_vreplgr2vr_h(xInc),
+                                      __lsx_vld(idx16, 0));
+        __m128i vx4    = __lsx_vreplgr2vr_w(4 * xInc);
+        __m128i vshuf8 = __lsx_vld(shuf8, 0);
+        __m128i v127   = __lsx_vreplgr2vr_h(127);
+
+        for (; i + 8 <= dstWidth && (xpos >> 16) + 32 < srcW; i += 8, xpos += 
8 * xInc) {
+            unsigned int lo = xpos & 0xFFFF;
+            unsigned int xx = xpos >> 16;
+
+            __m128i vc0 = __lsx_vadd_w(__lsx_vreplgr2vr_w(lo), vadd_w);
+            __m128i vc1 = __lsx_vadd_w(vc0, vx4);
+
+            __m128i vperm = __lsx_vshuf_b(__lsx_vsrli_w(vc1, 16),
+                                          __lsx_vsrli_w(vc0, 16), vshuf8);
+
+            __m128i valpha = __lsx_vsrli_h(__lsx_vadd_h(__lsx_vreplgr2vr_h(lo),
+                                                        vadd16), 9);
+
+            __m128i v10 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src1 + 
xx + 16, 0),
+                                                            __lsx_vld(src1 + 
xx, 0),
+                                                            vperm), 0);
+            __m128i v11 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src1 + 
xx + 17, 0),
+                                                            __lsx_vld(src1 + 
xx + 1, 0),
+                                                            vperm), 0);
+            __m128i v20 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src2 + 
xx + 16, 0),
+                                                            __lsx_vld(src2 + 
xx, 0),
+                                                            vperm), 0);
+            __m128i v21 = __lsx_vsllwil_hu_bu(__lsx_vshuf_b(__lsx_vld(src2 + 
xx + 17, 0),
+                                                            __lsx_vld(src2 + 
xx + 1, 0),
+                                                            vperm), 0);
+
+            __m128i w0 = __lsx_vsub_h(v127, valpha);
+            __lsx_vst(__lsx_vadd_h(__lsx_vmul_h(v10, w0), __lsx_vmul_h(v11, 
valpha)),
+                      dst1 + i, 0);
+            __lsx_vst(__lsx_vadd_h(__lsx_vmul_h(v20, w0), __lsx_vmul_h(v21, 
valpha)),
+                      dst2 + i, 0);
+        }
+    }
+
+    for (; i < dstWidth; i++) {
+        unsigned int xx     = xpos >> 16;
+        unsigned int xalpha = (xpos & 0xFFFF) >> 9;
+        dst1[i] = (src1[xx] * (xalpha ^ 127) + src1[xx + 1] * xalpha);
+        dst2[i] = (src2[xx] * (xalpha ^ 127) + src2[xx + 1] * xalpha);
+        xpos   += xInc;
+    }
+    for (i = dstWidth - 1; (i * (int64_t)xInc) >> 16 >= srcW - 1; i--) {
+        dst1[i] = src1[srcW - 1] * 128;
+        dst2[i] = src2[srcW - 1] * 128;
+    }
+}
diff --git a/libswscale/loongarch/swscale_init_loongarch.c 
b/libswscale/loongarch/swscale_init_loongarch.c
index 0c937b047f..ced73f2b0f 100644
--- a/libswscale/loongarch/swscale_init_loongarch.c
+++ b/libswscale/loongarch/swscale_init_loongarch.c
@@ -71,6 +71,12 @@ av_cold void ff_sws_init_swscale_loongarch(SwsInternal *c)
             c->hyScale = c->hcScale = c->dstBpc > 14 ? ff_hscale_16_to_19_lsx
                                                      : ff_hscale_16_to_15_lsx;
         }
+        if (c->srcBpc == 8 && c->dstBpc <= 14 &&
+            c->opts.flags & SWS_FAST_BILINEAR &&
+            c->lumXInc <= (1 << 18) && c->chrXInc <= (1 << 18)) {
+            c->hyscale_fast = ff_hyscale_fast_lsx;
+            c->hcscale_fast = ff_hcscale_fast_lsx;
+        }
     }
 #if HAVE_LASX
     if (have_lasx(cpu_flags)) {
diff --git a/libswscale/loongarch/swscale_loongarch.h 
b/libswscale/loongarch/swscale_loongarch.h
index a8297b6972..b2875ba70f 100644
--- a/libswscale/loongarch/swscale_loongarch.h
+++ b/libswscale/loongarch/swscale_loongarch.h
@@ -50,6 +50,13 @@ void ff_hscale_16_to_19_sub_lsx(SwsInternal *c, int16_t 
*_dst, int dstW,
                                 const uint8_t *_src, const int16_t *filter,
                                 const int32_t *filterPos, int filterSize, int 
sh);
 
+void ff_hyscale_fast_lsx(SwsInternal *c, int16_t *dst, int dstWidth,
+                         const uint8_t *src, int srcW, int xInc);
+
+void ff_hcscale_fast_lsx(SwsInternal *c, int16_t *dst1, int16_t *dst2,
+                         int dstWidth, const uint8_t *src1,
+                         const uint8_t *src2, int srcW, int xInc);
+
 void lumRangeFromJpeg_lsx(int16_t *dst, int width, uint32_t coeff, int64_t 
offset);
 void chrRangeFromJpeg_lsx(int16_t *dstU, int16_t *dstV, int width, uint32_t 
coeff, int64_t offset);
 void lumRangeToJpeg_lsx(int16_t *dst, int width, uint32_t coeff, int64_t 
offset);
diff --git a/tests/checkasm/sw_scale.c b/tests/checkasm/sw_scale.c
index 3c33785122..c9b946125e 100644
--- a/tests/checkasm/sw_scale.c
+++ b/tests/checkasm/sw_scale.c
@@ -457,10 +457,129 @@ static void check_hscale(void)
     sws_freeContext(sws);
 }
 
+/* Fast-bilinear horizontal scaling (SWS_FAST_BILINEAR, c->hyscale_fast /
+ * c->hcscale_fast): the optimized implementation must be bit-exact with
+ * the C reference. Catches e.g. swapped gather-position/weight tables in
+ * the LoongArch LSX implementation, which no FATE frame test covers.
+ *
+ * Only enabled on LoongArch: other archs' fast-bilinear implementations
+ * are not guaranteed bit-exact with the C reference (e.g. x86 MMXEXT uses
+ * complementary weights), so a cross-arch comparison would fail there. */
+#if ARCH_LOONGARCH64
+static void check_hyscale_fast(void)
+{
+#define HSCALE_FAST_SRC_SIZE 4096
+    static const int dstW_list[] = { 8, 16, 63, 100, 255, 256, 300, 511, 512, 
1024 };
+    static const int xInc_list[] = { 32768, 65536, 83886, 98304, 131072, 
200000 };
+    LOCAL_ALIGNED_32(uint8_t, src, [HSCALE_FAST_SRC_SIZE + 32]);
+    LOCAL_ALIGNED_32(int16_t, dst0, [2048]);
+    LOCAL_ALIGNED_32(int16_t, dst1, [2048]);
+    SwsContext *sws;
+    SwsInternal *c;
+    int i, j;
+
+    declare_func(void, SwsInternal *c, int16_t *dst, int dstWidth,
+                 const uint8_t *src, int srcW, int xInc);
+
+    sws = sws_alloc_context();
+    if (!sws || sws_init_context(sws, NULL, NULL) < 0)
+        fail();
+
+    c = sws_internal(sws);
+    c->srcBpc = 8;
+    c->dstBpc = 8;
+    c->opts.flags  = SWS_FAST_BILINEAR;
+    c->lumXInc = c->chrXInc = 83886;
+    ff_sws_init_scale(c);
+
+    if (check_func(c->hyscale_fast, "hyscale_fast")) {
+        for (i = 0; i < FF_ARRAY_ELEMS(dstW_list); i++) {
+            for (j = 0; j < FF_ARRAY_ELEMS(xInc_list); j++) {
+                int dstW = dstW_list[i];
+                int xInc = xInc_list[j];
+                int srcW = FFMIN(HSCALE_FAST_SRC_SIZE,
+                                 ((dstW * xInc) >> 16) + 16);
+
+                randomize_buffers(src, srcW + 16);
+                memset(dst0, 0, dstW * sizeof(dst0[0]));
+                memset(dst1, 0, dstW * sizeof(dst1[0]));
+
+                call_ref(NULL, dst0, dstW, src, srcW, xInc);
+                call_new(NULL, dst1, dstW, src, srcW, xInc);
+                if (memcmp(dst0, dst1, dstW * sizeof(dst0[0])))
+                    fail();
+            }
+        }
+        bench_new(NULL, dst1, 300, src, 512, 83886);
+    }
+    sws_freeContext(sws);
+}
+
+static void check_hcscale_fast(void)
+{
+#define HCSCALE_FAST_SRC_SIZE 4096
+    static const int dstW_list[] = { 8, 16, 63, 100, 255, 256, 300, 511, 512, 
1024 };
+    static const int xInc_list[] = { 32768, 65536, 83886, 98304, 131072, 
200000 };
+    LOCAL_ALIGNED_32(uint8_t, src1, [HCSCALE_FAST_SRC_SIZE + 32]);
+    LOCAL_ALIGNED_32(uint8_t, src2, [HCSCALE_FAST_SRC_SIZE + 32]);
+    LOCAL_ALIGNED_32(int16_t, dst0, [2048]);
+    LOCAL_ALIGNED_32(int16_t, dst1, [2048]);
+    LOCAL_ALIGNED_32(int16_t, dst2, [2048]);
+    LOCAL_ALIGNED_32(int16_t, dst3, [2048]);
+    SwsContext *sws;
+    SwsInternal *c;
+    int i, j;
+
+    declare_func(void, SwsInternal *c, int16_t *dst1, int16_t *dst2, int 
dstWidth,
+                 const uint8_t *src1, const uint8_t *src2, int srcW, int xInc);
+
+    sws = sws_alloc_context();
+    if (!sws || sws_init_context(sws, NULL, NULL) < 0)
+        fail();
+
+    c = sws_internal(sws);
+    c->srcBpc = 8;
+    c->dstBpc = 8;
+    c->opts.flags  = SWS_FAST_BILINEAR;
+    c->lumXInc = c->chrXInc = 83886;
+    ff_sws_init_scale(c);
+
+    if (check_func(c->hcscale_fast, "hcscale_fast")) {
+        for (i = 0; i < FF_ARRAY_ELEMS(dstW_list); i++) {
+            for (j = 0; j < FF_ARRAY_ELEMS(xInc_list); j++) {
+                int dstW = dstW_list[i];
+                int xInc = xInc_list[j];
+                int srcW = FFMIN(HCSCALE_FAST_SRC_SIZE,
+                                 ((dstW * xInc) >> 16) + 16);
+
+                randomize_buffers(src1, srcW + 16);
+                randomize_buffers(src2, srcW + 16);
+                memset(dst0, 0, dstW * sizeof(dst0[0]));
+                memset(dst1, 0, dstW * sizeof(dst1[0]));
+                memset(dst2, 0, dstW * sizeof(dst2[0]));
+                memset(dst3, 0, dstW * sizeof(dst3[0]));
+
+                call_ref(NULL, dst0, dst2, dstW, src1, src2, srcW, xInc);
+                call_new(NULL, dst1, dst3, dstW, src1, src2, srcW, xInc);
+                if (memcmp(dst0, dst1, dstW * sizeof(dst0[0])))
+                    fail();
+            }
+        }
+        bench_new(NULL, dst1, dst2, 300, src1, src2, 512, 83886);
+    }
+    sws_freeContext(sws);
+}
+#endif /* ARCH_LOONGARCH64 */
+
 void checkasm_check_sw_scale(void)
 {
     check_hscale();
     report("hscale");
+#if ARCH_LOONGARCH64
+    check_hyscale_fast();
+    check_hcscale_fast();
+    report("hscale_fast");
+#endif
     check_yuv2yuv1(0);
     check_yuv2yuv1(1);
     report("yuv2yuv1");
-- 
2.52.0

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

Reply via email to