Commit 8e940665d0 for ffmpeg
commit 8e940665d01195ea6a52ae19bb95e8cf1d05ebcf
Author: jinbo <jinbo@loongson.cn>
Date: Mon Aug 31 17:38:20 2026 +0800
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
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..1e4f6586fa
--- /dev/null
+++ b/libswscale/loongarch/swscale_unscaled.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2026 Loongson Technology Co. Ltd.
+ * Contributed by Bo Jin(jinbo@loongson.cn)
+ *
+ * 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_w & 1) || (c->opts.dst_h & 1) ||
+ (c->opts.flags & (SWS_ACCURATE_RND | SWS_BITEXACT | SWS_FULL_CHR_H_INT)))
+ 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:
+ return;
+ }
+
+ c->dst_slice_align = 2;
+}
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..ccd96fb204 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 && HAVE_LSX
+ ff_get_unscaled_swscale_loongarch(c);
#endif
}