[PR] avcodec/riscv: add HEVC deblocking filter RVV implementation (PR #23880)

Deng-zewen via ffmpeg-devel <[email protected]> Thu, 23 Jul 2026 12:37:32 -0000
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178481025352.59.6874045205045748282@29965ddac10e>
PR #23880 opened by Deng-zewen
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23880
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23880.patch

avcodec/riscv: add HEVC deblocking filter RVV implementation

This PR adds RISC-V Vector (RVV) implementations of the HEVC
deblocking filter for 8-bit depth.

Two main filter types are implemented:
- Luma filter: dual-path (strong/weak) with group-level decisions,
  supporting both vertical and horizontal boundaries.
- Chroma filter: delta-based filter for both vertical and horizontal
  boundaries.

Benchmarks on SG2044 (VLEN=128):

  Horizontal:
    hevc_h_loop_filter_chroma8_rvv_i32:       21.5 (2.08x)
    hevc_h_loop_filter_luma8_strong_rvv_i32:  94.2 (1.87x)
    hevc_h_loop_filter_luma8_weak_rvv_i32:    23.9 (1.69x)
    hevc_h_loop_filter_luma8_skip_rvv_i32:    23.8 (1.70x)

  Vertical:
    hevc_v_loop_filter_luma8_strong_rvv_i32: 122.5 (1.30x)

Vertical chroma is kept as C fallback on VLEN=128 (0.61x vs C).



>From 64e72b7282b8edad8328aa1f90c44bcc1dd10588 Mon Sep 17 00:00:00 2001
From: "deng.zewen" <[email protected]>
Date: Thu, 23 Jul 2026 20:32:08 +0800
Subject: [PATCH] avcodec/riscv: add HEVC deblocking filter RVV implementation

---
 libavcodec/riscv/Makefile              |   3 +-
 libavcodec/riscv/hevcdsp_deblock_rvv.S | 632 +++++++++++++++++++++++++
 libavcodec/riscv/hevcdsp_init.c        |  19 +
 3 files changed, 653 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/riscv/hevcdsp_deblock_rvv.S

diff --git a/libavcodec/riscv/Makefile b/libavcodec/riscv/Makefile
index ff063afc35..bfa514b6d7 100644
--- a/libavcodec/riscv/Makefile
+++ b/libavcodec/riscv/Makefile
@@ -38,7 +38,8 @@ RVV-OBJS-$(CONFIG_H264PRED)  += riscv/h264_intrapred_rvv.o
 OBJS-$(CONFIG_H264QPEL) += riscv/h264qpel_init.o
 RVV-OBJS-$(CONFIG_H264QPEL) += riscv/h264qpel_rvv.o
 OBJS-$(CONFIG_HEVC_DECODER) += riscv/hevcdsp_init.o
-RVV-OBJS-$(CONFIG_HEVC_DECODER) += riscv/hevcdsp_idct_rvv.o
+RVV-OBJS-$(CONFIG_HEVC_DECODER) += riscv/hevcdsp_idct_rvv.o \
+                                riscv/hevcdsp_deblock_rvv.o
 RVV-OBJS-$(CONFIG_HEVC_DECODER)  += riscv/h26x/h2656_inter_rvv.o
 OBJS-$(CONFIG_HUFFYUV_DECODER) += riscv/huffyuvdsp_init.o
 RVV-OBJS-$(CONFIG_HUFFYUV_DECODER) += riscv/huffyuvdsp_rvv.o
diff --git a/libavcodec/riscv/hevcdsp_deblock_rvv.S b/libavcodec/riscv/hevcdsp_deblock_rvv.S
new file mode 100644
index 0000000000..3531d17103
--- /dev/null
+++ b/libavcodec/riscv/hevcdsp_deblock_rvv.S
@@ -0,0 +1,632 @@
+/* -*-riscv64-*-
+ *
+ * Copyright (c) 2026 Deng Zewen <[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 "libavutil/riscv/asm.S"
+
+.macro vabs d0, s0, t0
+    vrsub.vi    \t0, \s0, 0
+    vmax.vv     \d0, \s0, \t0
+.endm
+
+.macro uabd d0, s0, s1, t0
+    vmaxu.vv        \d0, \s0, \s1
+    vminu.vv        \t0, \s0, \s1
+    vsub.vv         \d0, \d0, \t0
+.endm
+
+.macro transpose16_8x8 r0, r1, r2, r3, r4, r5, r6, r7, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9
+        vid.v           \tmp9
+        vand.vi         \tmp8, \tmp9, 0x1
+        vmsne.vi        v0, \tmp8, 0
+
+        // Stage 1: interleave even/odd rows
+        vslideup.vi     \tmp0, \r1, 1
+        vmerge.vvm      \tmp4, \r0, \tmp0, v0
+        vslideup.vi     \tmp1, \r3, 1
+        vmerge.vvm      \tmp5, \r2, \tmp1, v0
+
+        vslideup.vi     \tmp2, \r5, 1
+        vmerge.vvm      \tmp6, \r4, \tmp2, v0
+        vslideup.vi     \tmp3, \r7, 1
+        vmerge.vvm      \tmp7, \r6, \tmp3, v0
+
+        vslidedown.vi   \tmp0, \r0, 1
+        vmerge.vvm      \r0, \tmp0, \r1, v0
+        vslidedown.vi   \tmp1, \r2, 1
+        vmerge.vvm      \r2, \tmp1, \r3, v0
+
+        vslidedown.vi   \tmp2, \r4, 1
+        vmerge.vvm      \r4, \tmp2, \r5, v0
+        vslidedown.vi   \tmp3, \r6, 1
+        vmerge.vvm      \r6, \tmp3, \r7, v0
+
+        // Stage 2: interleave pairs
+        vand.vi         \tmp8, \tmp9, 0x2
+        vmsne.vi        v0, \tmp8, 0
+
+        vslideup.vi     \tmp0, \tmp5, 2
+        vmerge.vvm      \tmp0, \tmp4, \tmp0, v0
+        vslideup.vi     \tmp1, \tmp7, 2
+        vmerge.vvm      \tmp1, \tmp6, \tmp1, v0
+
+        vslidedown.vi   \tmp2, \tmp4, 2
+        vmerge.vvm      \tmp2, \tmp2, \tmp5, v0
+        vslidedown.vi   \tmp3, \tmp6, 2
+        vmerge.vvm      \tmp3, \tmp3, \tmp7, v0
+
+        vslideup.vi     \r1, \r2, 2
+        vmerge.vvm      \r1, \r0, \r1, v0
+        vslideup.vi     \r3, \r6, 2
+        vmerge.vvm      \r3, \r4, \r3, v0
+
+        vslidedown.vi   \r5, \r0, 2
+        vmerge.vvm      \r5, \r5, \r2, v0
+        vslidedown.vi   \r7, \r4, 2
+        vmerge.vvm      \r7, \r7, \r6, v0
+
+        // Stage 3: interleave quads
+        vand.vi         \tmp8, \tmp9, 0x4
+        vmsne.vi        v0, \tmp8, 0
+
+        vslideup.vi     \tmp4, \tmp1, 4
+        vmerge.vvm      \tmp4, \tmp0, \tmp4, v0
+        vslideup.vi     \tmp5, \tmp3, 4
+        vmerge.vvm      \tmp5, \tmp2, \tmp5, v0
+
+        vslidedown.vi   \tmp6, \tmp0, 4
+        vmerge.vvm      \tmp6, \tmp6, \tmp1, v0
+        vslidedown.vi   \tmp7, \tmp2, 4
+        vmerge.vvm      \tmp7, \tmp7, \tmp3, v0
+
+        vslideup.vi     \tmp0, \r3, 4
+        vmerge.vvm      \tmp0, \r1, \tmp0, v0
+        vslideup.vi     \tmp1, \r7, 4
+        vmerge.vvm      \tmp1, \r5, \tmp1, v0
+
+        vslidedown.vi   \tmp2, \r1, 4
+        vmerge.vvm      \tmp2, \tmp2, \r3, v0
+        vslidedown.vi   \tmp3, \r5, 4
+        vmerge.vvm      \tmp3, \tmp3, \r7, v0
+.endm
+
+.macro hevc_loop_filter_luma_body
+       vmv.v.x     v16, a2            // beta
+
+        // v17 = [tc0 x4, tc1 x4]; v19 = tc25 = (tc*5+1)>>1; v20 = beta>>2
+        vmv.v.x     v17, a3
+        vmv.v.x     v24, t6
+        vslideup.vi v17, v24, 4
+        vsll.vi     v18, v17, 2
+        vadd.vv     v18, v18, v17
+        vadd.vi     v18, v18, 1
+        vsra.vi     v19, v18, 1
+        vsra.vi     v20, v16, 2
+
+        // dp0 = |P2 - 2*P1 + P0|, dq0 = |Q2 - 2*Q1 + Q0|, d0 = dp0 + dq0
+        vadd.vv     v21, v11, v9
+        vsll.vi     v22, v10, 1
+        vsub.vv     v21, v21, v22
+        vabs      v21, v21, v30
+
+        vadd.vv     v22, v14, v12
+        vsll.vi     v23, v13, 1
+        vsub.vv     v22, v22, v23
+        vabs      v22, v22, v30
+
+        vadd.vv     v23, v21, v22
+
+        // v24: per-lane mask for (d0<<1) < beta2
+        vsll.vi     v24, v23, 1
+        vmsltu.vv   v0, v24, v20
+        vmv.v.i     v24, 0
+        vmerge.vim  v24, v24, -1, v0
+
+        // Group decision: 4 lines per group; check d0+d3 < beta for rows {0,3} and {4,7}
+        vmv.x.s     t0, v23
+        vslidedown.vi v25, v23, 3
+        vmv.x.s     t1, v25
+        add         t0, t0, t1
+
+        vslidedown.vi v25, v23, 4
+        vmv.x.s     t2, v25
+        vslidedown.vi v25, v23, 7
+        vmv.x.s     t3, v25
+        add         t2, t2, t3
+
+        sltu        t0, t0, a2
+        sltu        t2, t2, a2
+        or          t5, t0, t2
+        beqz        t5, 4f
+
+        // v26: group enable mask (low 4 = group0, high 4 = group1)
+        neg         t0, t0
+        li          t1, 0xFFFF
+        and         t0, t0, t1
+        neg         t2, t2
+        and         t2, t2, t1
+        vmv.v.x     v26, t0
+        vmv.v.x     v25, t2
+        vslideup.vi v26, v25, 4
+
+        // Strong filter conditions
+        vsra.vi     v27, v20, 1        // beta_3 = beta2 >> 1
+
+        // |P3-P0| + |Q3-Q0| < beta_3
+        uabd      v28, v8, v11, v30
+        uabd      v29, v15, v12, v30
+        vadd.vv     v28, v28, v29
+        vmsltu.vv   v0, v28, v27
+        vmv.v.i     v28, 0
+        vmerge.vim  v28, v28, -1, v0
+        vand.vv     v24, v24, v28
+
+        // |P0-Q0| < tc25
+        uabd      v28, v11, v12, v30
+        vmsltu.vv   v0, v28, v19
+        vmv.v.i     v28, 0
+        vmerge.vim  v28, v28, -1, v0
+        vand.vv     v24, v24, v28
+
+        // Per-group strong flag: both row0 and row3 (or row4 and row7) must pass
+        vmv.x.s     t0, v24
+        vslidedown.vi v28, v24, 3
+        vmv.x.s     t1, v28
+        and         t0, t0, t1
+        vslidedown.vi v28, v24, 4
+        vmv.x.s     t2, v28
+        vslidedown.vi v28, v24, 7
+        vmv.x.s     t3, v28
+        and         t2, t2, t3
+
+        mv          a3, t0             // group0 strong flag
+        mv          a4, t2             // group1 strong flag
+
+        // v1: per-lane strong/weak selector (1=strong, 0=weak)
+        vmv.v.x     v1, a3
+        vmv.v.x     v25, a4
+        vslideup.vi v1, v25, 4
+
+        // Skip nd_p/nd_q if both groups are strong
+        li          t4, 0xFFFF
+        bne         a3, t4, 0f
+        bne         a4, t4, 0f
+        j           1f
+
+0:
+        // Weak filter: compute nd_p, nd_q
+        srli        t5, a2, 1
+        add         t5, t5, a2
+        srli        t5, t5, 3          // threshold = (beta + beta/2) >> 3
+
+        vslidedown.vi v25, v21, 3
+        vadd.vv      v25, v25, v21
+        vmv.x.s     t0, v25
+        vslidedown.vi v2, v25, 4
+        vmv.x.s     t1, v2
+
+        vslidedown.vi v25, v22, 3
+        vadd.vv      v25, v25, v22
+        vmv.x.s     t2, v25
+        vslidedown.vi v2, v25, 4
+        vmv.x.s     t3, v2
+
+        sltu        t0, t0, t5
+        sltu        t1, t1, t5
+        sltu        t2, t2, t5
+        sltu        t3, t3, t5
+
+        neg         t0, t0
+        neg         t1, t1
+        neg         t2, t2
+        neg         t3, t3
+
+        // v28 = nd_p mask, v29 = nd_q mask
+        vmv.v.x     v28, t0
+        vmv.v.x     v25, t1
+        vslideup.vi v28, v25, 4
+
+        vmv.v.x     v29, t2
+        vmv.v.x     v25, t3
+        vslideup.vi v29, v25, 4
+
+        // delta0 = Clip((9*(q0-p0) - 3*(q1-p1) + 8) >> 4, -tc, tc)
+        vsub.vv     v30, v12, v11
+        vsll.vi     v31, v30, 3
+        vadd.vv     v30, v30, v31
+        vsub.vv     v31, v13, v10
+        vsll.vi     v27, v31, 1
+        vadd.vv     v31, v27, v31
+        vsub.vv     v30, v30, v31
+        vadd.vi     v30, v30, 8
+        vsra.vi     v30, v30, 4
+
+        // Apply only if |delta0| < 10*tc and group enabled
+        vsll.vi     v27, v17, 1
+        vsll.vi     v24, v17, 3
+        vadd.vv     v27, v27, v24      // 10*tc
+        vabs      v24, v30, v31
+        vmsltu.vv   v0, v24, v27
+        vmv.v.i     v27, 0
+        vmerge.vim  v27, v27, -1, v0
+        vand.vv     v27, v27, v26
+
+        vneg.v      v31, v17
+        vmax.vv     v30, v30, v31
+        vmin.vv     v30, v30, v17
+
+        // deltap1 = Clip(((p2+p0+1)>>1 - p1 + delta0) >> 1, -tc/2, tc/2)
+        vadd.vv     v24, v9, v11
+        vadd.vi     v24, v24, 1
+        vsrl.vi     v24, v24, 1
+        vsub.vv     v24, v24, v10
+        vadd.vv     v24, v24, v30
+        vsra.vi     v24, v24, 1
+
+        // deltaq1 = Clip(((q2+q0+1)>>1 - q1 - delta0) >> 1, -tc/2, tc/2)
+        vadd.vv     v25, v14, v12
+        vadd.vi     v25, v25, 1
+        vsrl.vi     v25, v25, 1
+        vsub.vv     v25, v25, v13
+        vsub.vv     v25, v25, v30
+        vsra.vi     v25, v25, 1
+
+        vsra.vi     v31, v17, 1        // tc/2
+        vneg.v      v18, v31
+        vmax.vv     v24, v24, v18
+        vmin.vv     v24, v24, v31
+        vmax.vv     v25, v25, v18
+        vmin.vv     v25, v25, v31
+
+        vand.vv     v24, v24, v28      // apply nd_p
+        vand.vv     v25, v25, v29      // apply nd_q
+        vand.vv     v24, v24, v27
+        vand.vv     v30, v30, v27
+        vand.vv     v25, v25, v27
+
+        // Weak filter results
+        vadd.vv     v18, v10, v24      // P1' weak
+        vadd.vv     v19, v11, v30      // P0' weak
+        vsub.vv     v20, v12, v30      // Q0' weak
+        vadd.vv     v21, v13, v25      // Q1' weak
+
+1:
+        // Strong filter: tc2 = 2*tc
+        vsll.vi     v24, v17, 1
+        vneg.v      v25, v24
+
+        // P0' = p0 + Clip((p2+2*p1+2*p0+2*q0+q1+4)>>3 - p0, -tc2, tc2)
+        vadd.vv     v26, v10, v11
+        vadd.vv     v26, v26, v12
+        vsll.vi     v26, v26, 1
+        vadd.vv     v27, v9, v13
+        vadd.vv     v26, v26, v27
+        vadd.vi     v26, v26, 4
+        vsra.vi     v26, v26, 3
+        vsub.vv     v26, v26, v11
+
+        // P1' = p1 + Clip((p2+p1+p0+q0+2)>>2 - p1, -tc2, tc2)
+        vadd.vv     v27, v9, v10
+        vadd.vv     v28, v11, v12
+        vadd.vv     v27, v27, v28
+        vadd.vi     v27, v27, 2
+        vsra.vi     v27, v27, 2
+        vsub.vv     v27, v27, v10
+
+        // P2' = p2 + Clip((2*p3+3*p2+p1+p0+q0+4)>>3 - p2, -tc2, tc2)
+        vadd.vv     v28, v8, v9
+        vsll.vi     v28, v28, 1
+        vadd.vv     v29, v11, v12
+        vadd.vv     v28, v28, v29
+        vadd.vv     v29, v9, v10
+        vadd.vv     v28, v28, v29
+        vadd.vi     v28, v28, 4
+        vsra.vi     v28, v28, 3
+        vsub.vv     v28, v28, v9
+
+        // Q0' = q0 + Clip((p1+2*p0+2*q0+2*q1+q2+4)>>3 - q0, -tc2, tc2)
+        vadd.vv     v29, v11, v12
+        vadd.vv     v29, v29, v13
+        vsll.vi     v29, v29, 1
+        vadd.vv     v30, v10, v14
+        vadd.vv     v29, v29, v30
+        vadd.vi     v29, v29, 4
+        vsra.vi     v29, v29, 3
+        vsub.vv     v29, v29, v12
+
+        // Q1' = q1 + Clip((p0+q0+q1+q2+2)>>2 - q1, -tc2, tc2)
+        vadd.vv     v30, v14, v13
+        vadd.vv     v31, v11, v12
+        vadd.vv     v30, v30, v31
+        vadd.vi     v30, v30, 2
+        vsra.vi     v30, v30, 2
+        vsub.vv     v30, v30, v13
+
+        // Q2' = q2 + Clip((2*q3+3*q2+q1+q0+p0+4)>>3 - q2, -tc2, tc2)
+        vadd.vv     v31, v15, v14
+        vsll.vi     v31, v31, 1
+        vadd.vv     v0,  v14, v13
+        vadd.vv     v31, v31, v0
+        vadd.vv     v0,  v11, v12
+        vadd.vv     v31, v31, v0
+        vadd.vi     v31, v31, 4
+        vsra.vi     v31, v31, 3
+        vsub.vv     v31, v31, v14
+
+        // Clip all deltas to [-tc2, tc2]
+        vmax.vv     v26, v26, v25
+        vmin.vv     v26, v26, v24
+        vmax.vv     v27, v27, v25
+        vmin.vv     v27, v27, v24
+        vmax.vv     v28, v28, v25
+        vmin.vv     v28, v28, v24
+        vmax.vv     v29, v29, v25
+        vmin.vv     v29, v29, v24
+        vmax.vv     v30, v30, v25
+        vmin.vv     v30, v30, v24
+        vmax.vv     v31, v31, v25
+        vmin.vv     v31, v31, v24
+
+        vadd.vv     v28, v28, v9       // P2' strong
+        vadd.vv     v27, v27, v10      // P1' strong
+        vadd.vv     v26, v26, v11      // P0' strong
+        vadd.vv     v29, v29, v12      // Q0' strong
+        vadd.vv     v30, v30, v13      // Q1' strong
+        vadd.vv     v31, v31, v14      // Q2' strong
+
+        // If both groups strong, use strong results directly
+        bne         a3, t4, 2f
+        bne         a4, t4, 2f
+        vmv.v.v     v1, v28
+        vmv.v.v     v2, v27
+        vmv.v.v     v3, v26
+        vmv.v.v     v4, v29
+        vmv.v.v     v5, v30
+        vmv.v.v     v6, v31
+        j           3f
+
+2:
+        // Merge strong/weak results using v1 selector mask
+        vmsne.vi    v0, v1, 0
+        vmerge.vvm  v1,  v9,  v28, v0  // P2: strong=v28, weak=orig
+        vmerge.vvm  v2,  v18, v27, v0  // P1
+        vmerge.vvm  v3,  v19, v26, v0  // P0
+        vmerge.vvm  v4,  v20, v29, v0  // Q0
+        vmerge.vvm  v5,  v21, v30, v0  // Q1
+        vmerge.vvm  v6,  v14, v31, v0  // Q2: strong=v31, weak=orig
+
+3:
+        // Clamp to [0, 255]
+        vmax.vx     v18, v1, zero
+        vmax.vx     v19, v2, zero
+        vmax.vx     v20, v3, zero
+        vmax.vx     v21, v4, zero
+        vmax.vx     v22, v5, zero
+        vmax.vx     v23, v6, zero
+.endm
+
+.macro hevc_loop_filter_luma_8_rvv dir
+	func ff_hevc_\dir\()_loop_filter_luma_8_rvv, zve32x
+	lpad            0
+.ifc \dir, v
+        addi        a0, a0, -4
+.else
+        slli        t0, a1, 2
+        sub         a0, a0, t0
+.endif
+        mv          a5, a0              // save base address for store
+        lw          t6, 4(a3)           // tc[1]
+        lw          a3, 0(a3)           // tc[0]
+        or          t0, a3, t6
+        beqz        t0, 4f              // skip if both tc == 0
+
+        // Load 8 rows of 8 pixels
+        vsetivli    zero, 8, e8, m1, ta, ma
+        vle8.v      v0, (a0)
+        add         a0, a0, a1
+        vle8.v      v1, (a0)
+        add         a0, a0, a1
+        vle8.v      v2, (a0)
+        add         a0, a0, a1
+        vle8.v      v3, (a0)
+        add         a0, a0, a1
+        vle8.v      v4, (a0)
+        add         a0, a0, a1
+        vle8.v      v5, (a0)
+        add         a0, a0, a1
+        vle8.v      v6, (a0)
+        add         a0, a0, a1
+        vle8.v      v7, (a0)
+.ifc \dir, v
+        // Widen to 16-bit and transpose (vertical boundary -> horizontal)
+        vsetivli    zero, 8, e16, m1, ta, ma
+        vzext.vf2   v16,  v0
+        vzext.vf2   v17,  v1
+        vzext.vf2   v18, v2
+        vzext.vf2   v19, v3
+        vzext.vf2   v20, v4
+        vzext.vf2   v21, v5
+        vzext.vf2   v22, v6
+        vzext.vf2   v23, v7
+
+        transpose16_8x8 v16, v17, v18, v19, v20, v21, v22, v23, v9, v11, v13, v15, v8, v10, v12, v14, v1, v2
+.else
+        // Widen to 16-bit (horizontal boundary: pixels already in order)
+        vsetivli    zero, 8, e16, m1, ta, ma
+        vzext.vf2   v8,  v0   // P3
+        vzext.vf2   v9,  v1   // P2
+        vzext.vf2   v10, v2   // P1
+        vzext.vf2   v11, v3   // P0
+        vzext.vf2   v12, v4   // Q0
+        vzext.vf2   v13, v5   // Q1
+        vzext.vf2   v14, v6   // Q2
+        vzext.vf2   v15, v7   // Q3
+.endif
+        hevc_loop_filter_luma_body
+.ifc \dir, v
+        // Transpose back and narrow to 8-bit
+        transpose16_8x8 v8, v18, v19, v20, v21, v22, v23, v15, v25, v27, v29, v31, v24, v26, v28, v30, v1, v2
+
+        vsetivli    zero, 8, e8, mf2, ta, ma
+        vnclipu.wi  v0, v24, 0
+        vnclipu.wi  v1, v25, 0
+        vnclipu.wi  v2, v26, 0
+        vnclipu.wi  v3, v27, 0
+        vnclipu.wi  v4, v28, 0
+        vnclipu.wi  v5, v29, 0
+        vnclipu.wi  v6, v30, 0
+        vnclipu.wi  v7, v31, 0
+
+        // Store
+        mv          a0, a5
+        vse8.v      v0, (a0)
+        add         a0, a0, a1
+        vse8.v      v1, (a0)
+        add         a0, a0, a1
+        vse8.v      v2, (a0)
+        add         a0, a0, a1
+        vse8.v      v3, (a0)
+        add         a0, a0, a1
+        vse8.v      v4, (a0)
+        add         a0, a0, a1
+        vse8.v      v5, (a0)
+        add         a0, a0, a1
+        vse8.v      v6, (a0)
+        add         a0, a0, a1
+        vse8.v      v7, (a0)
+.else
+        vsetivli    zero, 8, e8, mf2, ta, ma
+        vnclipu.wi  v1, v18, 0
+        vnclipu.wi  v2, v19, 0
+        vnclipu.wi  v3, v20, 0
+        vnclipu.wi  v4, v21, 0
+        vnclipu.wi  v5, v22, 0
+        vnclipu.wi  v6, v23, 0
+
+        // Store (skip P3 row)
+        mv          a0, a5
+        add         a0, a0, a1
+        vse8.v      v1, (a0)
+        add         a0, a0, a1
+        vse8.v      v2, (a0)
+        add         a0, a0, a1
+        vse8.v      v3, (a0)
+        add         a0, a0, a1
+        vse8.v      v4, (a0)
+        add         a0, a0, a1
+        vse8.v      v5, (a0)
+        add         a0, a0, a1
+        vse8.v      v6, (a0)
+.endif
+4:
+        ret
+endfunc
+.endm
+
+hevc_loop_filter_luma_8_rvv h
+hevc_loop_filter_luma_8_rvv v
+
+.macro hevc_loop_filter_chroma_8_rvv dir
+	func ff_hevc_\dir\()_loop_filter_chroma_8_rvv, zve32x
+	lpad            0
+        lw          t0, 0(a2)           // tc[0]
+        lw          t1, 4(a2)           // tc[1]
+        or          t2, t0, t1
+        beqz        t2, 1f              // skip if both tc == 0
+
+.ifc \dir, v
+        addi        a0, a0, -2
+.else
+        slli        t2, a1, 1
+        sub         a0, a0, t2
+.endif
+        mv          t3, a0              // save base address
+
+        // Load p1, p0, q0, q1
+        vsetivli    zero, 8, e8, m1, ta, ma
+.ifc \dir, v
+        vlse8.v     v0, (a0), a1
+        addi        a0, a0, 1
+        vlse8.v     v1, (a0), a1
+        addi        a0, a0, 1
+        vlse8.v     v2, (a0), a1
+        addi        a0, a0, 1
+        vlse8.v     v3, (a0), a1
+.else
+        vle8.v      v0, (a0)
+        add         a0, a0, a1
+        vle8.v      v1, (a0)
+        add         a0, a0, a1
+        vle8.v      v2, (a0)
+        add         a0, a0, a1
+        vle8.v      v3, (a0)
+.endif
+        vsetivli    zero, 8, e16, m1, ta, ma
+        vzext.vf2   v20, v0             // p1
+        vzext.vf2   v21, v1             // p0
+        vzext.vf2   v22, v2             // q0
+        vzext.vf2   v23, v3             // q1
+
+        // v16 = [tc0 x4, tc1 x4], v17 = -tc
+        vmv.v.x     v16, t0
+        vmv.v.x     v17, t1
+        vslideup.vi v16, v17, 4
+        vneg.v      v17, v16
+
+        // delta = Clip((((q0-p0) << 2) + (p1-q1) + 4) >> 3, -tc, tc)
+        vsub.vv     v24, v22, v21
+        vsll.vi     v24, v24, 2
+        vsub.vv     v25, v20, v23
+        vadd.vv     v24, v24, v25
+        vadd.vi     v24, v24, 4
+        vsra.vi     v24, v24, 3
+        vmax.vv     v24, v24, v17
+        vmin.vv     v24, v24, v16
+
+        vadd.vv     v21, v21, v24       // p0 + delta
+        vsub.vv     v22, v22, v24       // q0 - delta
+
+        // Clamp to [0, 255] and narrow to 8-bit
+        vmax.vx     v21, v21, zero
+        vmax.vx     v22, v22, zero
+
+        vsetivli    zero, 8, e8, mf2, ta, ma
+        vnclipu.wi  v1, v21, 0
+        vnclipu.wi  v2, v22, 0
+
+.ifc \dir, v
+        addi        a0, t3, 1           // p0 column
+        vsse8.v     v1, (a0), a1
+        addi        a0, a0, 1           // q0 column
+        vsse8.v     v2, (a0), a1
+.else
+        add         a0, t3, a1          // p0 row
+        vse8.v      v1, (a0)
+        add         a0, a0, a1          // q0 row
+        vse8.v      v2, (a0)
+.endif
+
+1:
+        ret
+endfunc
+.endm
+
+hevc_loop_filter_chroma_8_rvv h
+hevc_loop_filter_chroma_8_rvv v
diff --git a/libavcodec/riscv/hevcdsp_init.c b/libavcodec/riscv/hevcdsp_init.c
index 597b635248..1cafe05eef 100644
--- a/libavcodec/riscv/hevcdsp_init.c
+++ b/libavcodec/riscv/hevcdsp_init.c
@@ -36,6 +36,19 @@ void ff_hevc_add_residual_16x16_8_rvv(uint8_t *_dst, const int16_t *coeffs,
 void ff_hevc_add_residual_32x32_8_rvv(uint8_t *_dst, const int16_t *coeffs,
                                        ptrdiff_t stride);
 
+void ff_hevc_v_loop_filter_luma_8_rvv(uint8_t *pix, ptrdiff_t stride,
+                                       int beta, const int32_t *tc,
+                                       const uint8_t *no_p, const uint8_t *no_q);
+void ff_hevc_h_loop_filter_luma_8_rvv(uint8_t *pix, ptrdiff_t stride,
+                                       int beta, const int32_t *tc,
+                                       const uint8_t *no_p, const uint8_t *no_q);
+void ff_hevc_v_loop_filter_chroma_8_rvv(uint8_t *pix, ptrdiff_t stride,
+                                         const int32_t *tc, const uint8_t *no_p,
+                                         const uint8_t *no_q);
+void ff_hevc_h_loop_filter_chroma_8_rvv(uint8_t *pix, ptrdiff_t stride,
+                                         const int32_t *tc, const uint8_t *no_p,
+                                         const uint8_t *no_q);
+
 #define RVV_FNASSIGN(member, v, h, fn, ext) \
         member[1][v][h] = ff_h2656_put_pixels_##8_##ext;  \
         member[3][v][h] = ff_h2656_put_pixels_##8_##ext;  \
@@ -83,6 +96,12 @@ void ff_hevc_dsp_init_riscv(HEVCDSPContext *c, const int bit_depth)
                 c->add_residual[1]             = ff_hevc_add_residual_8x8_8_rvv;
                 c->add_residual[2]             = ff_hevc_add_residual_16x16_8_rvv;
                 c->add_residual[3]             = ff_hevc_add_residual_32x32_8_rvv;
+
+                c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_8_rvv;
+                c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_8_rvv;
+                /* C is faster on VLEN=128 (0.61x), so keep the C path for vertical chroma */
+                // c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_8_rvv;
+                c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_8_rvv;
                 break;
             default:
                 break;
-- 
2.52.0

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