[PATCH v2 2/2] lavc/vvc: add AVX2 planar intra prediction

Gudikandula Samith via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
Implements pred_planar for x86-64 AVX2 at 8 bit.

Benchmarks on AMD Ryzen 7 5825U:
  vvc_pred_planar_4x4_8_c:         86.1
  vvc_pred_planar_4x4_8_avx2:      30.4 (2.81x)
  vvc_pred_planar_8x8_8_c:        307.0
  vvc_pred_planar_8x8_8_avx2:      56.3 (5.39x)
  vvc_pred_planar_16x16_8_c:     1116.0
  vvc_pred_planar_16x16_8_avx2:   199.3 (5.46x)
  vvc_pred_planar_32x32_8_c:     3328.7
  vvc_pred_planar_32x32_8_avx2:   736.6 (4.01x)
  vvc_pred_planar_64x64_8_c:    11554.9
  vvc_pred_planar_64x64_8_avx2:  2761.3 (4.02x)

Signed-off-by: Gudikandula Samith <[email protected]>
---
 libavcodec/x86/vvc/Makefile   |   1 +
 libavcodec/x86/vvc/dsp_init.c |  13 ++
 libavcodec/x86/vvc/intra.asm  | 233 ++++++++++++++++++++++++++++++++++
 3 files changed, 247 insertions(+)
 create mode 100644 libavcodec/x86/vvc/intra.asm

diff --git a/libavcodec/x86/vvc/Makefile b/libavcodec/x86/vvc/Makefile
index 0cebfb4e9e..b24ea6e692 100644
--- a/libavcodec/x86/vvc/Makefile
+++ b/libavcodec/x86/vvc/Makefile
@@ -4,6 +4,7 @@ clean::
 X86ASM-OBJS-$(CONFIG_VVC_DECODER)      += x86/vvc/dsp_init.o        \
                                           x86/vvc/alf.o             \
                                           x86/vvc/dmvr.o            \
+                                          x86/vvc/intra.o           \
                                           x86/vvc/mc.o              \
                                           x86/vvc/of.o              \
                                           x86/vvc/sad.o             \
diff --git a/libavcodec/x86/vvc/dsp_init.c b/libavcodec/x86/vvc/dsp_init.c
index 6802294795..4f187e1eb1 100644
--- a/libavcodec/x86/vvc/dsp_init.c
+++ b/libavcodec/x86/vvc/dsp_init.c
@@ -292,6 +292,16 @@ void bf(ff_vvc_alf_filter_chroma, bd, opt)(uint8_t *dst, ptrdiff_t dst_stride, \
     c->alf.classify       = bf(vvc_alf_classify, bd, opt);                     \
 } while (0)
 
+#define INTRA_PROTOTYPE(bd, opt)                                               \
+void ff_vvc_pred_planar_##bd##_##opt(uint8_t *src, const uint8_t *top,         \
+    const uint8_t *left, int w, int h, ptrdiff_t stride);
+
+INTRA_PROTOTYPE(8, avx2)
+
+#define INTRA_INIT(bd, opt) do {                                               \
+    c->intra.pred_planar = ff_vvc_pred_planar_##bd##_##opt;                    \
+} while (0)
+
 #endif
 
 
@@ -321,6 +331,9 @@ av_cold void ff_vvc_dsp_init_x86(VVCDSPContext *const c, const int bd)
             // filter
             ALF_INIT(8, avx2);
             SAO_INIT(8, avx2);
+
+            // intra
+            INTRA_INIT(8, avx2);
         }
 #endif
         break;
diff --git a/libavcodec/x86/vvc/intra.asm b/libavcodec/x86/vvc/intra.asm
new file mode 100644
index 0000000000..1b3a8099a6
--- /dev/null
+++ b/libavcodec/x86/vvc/intra.asm
@@ -0,0 +1,233 @@
+; /*
+; * Provide SIMD planar intra prediction functions for VVC decoding
+; *
+; * Copyright (c) 2026 Gudikandula Samith <[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/x86/x86util.asm"
+
+SECTION_RODATA 32
+
+pw_wm1x_w4:  dw  3,  2,  1,  0,  0,  0,  0,  0
+pw_xp1_w4:   dw  1,  2,  3,  4,  0,  0,  0,  0
+pw_wm1x_w8:  dw  7,  6,  5,  4,  3,  2,  1,  0
+pw_xp1_w8:   dw  1,  2,  3,  4,  5,  6,  7,  8
+pw_wm1x_w16: dw  15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0  ; (w - 1 - x)
+pw_xp1_w16:  dw  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16  ; (x + 1)
+pw_wm1x_w32: dw  31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, \
+                 15, 14, 13, 12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1,  0
+pw_xp1_w32:  dw  1,  2,  3,  4,  5,  6,  7,  8, 9,  10, 11, 12, 13, 14, 15, 16, \
+                 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+pw_wm1x_w64: dw  63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, \
+                 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, \
+                 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, \
+                 15, 14, 13, 12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1,  0
+pw_xp1_w64:  dw  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, \
+                 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, \
+                 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, \
+                 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
+
+pw_1:        times 16 dw 1
+
+
+SECTION .text
+
+%if ARCH_X86_64
+%if HAVE_AVX2_EXTERNAL
+
+INIT_YMM avx2
+
+; Per-row scalar that still has to be loaded every row:
+; m10 = C = left[y]
+; A = (h-1-y) in m8 and B = (y+1)*left[h] in m9 are advanced iteratively
+; at the end of each row loop (m8 -= 1, m9 += left[h]).
+%macro ROW_CONSTANTS 0
+    movzx          eax, byte [leftq + r11]
+    movd          xm10, eax
+    vpbroadcastw   m10, xm10
+%endmacro
+
+; Block-constant setup shared by every width.
+;  %1 = logw (immediate), %2 = top[w] byte offset (=width)
+%macro PLANAR_SETUP 2
+    bsr            r6d, hd
+    lea            r7d, [r6d + %1 + 1]
+    mov            r8d, hd                ; r8 = h (mov zero-extends into r8)
+    movzx         r10d, byte [leftq + r8] ; left[h]  (index by h before shifting)
+    shl            r8d, %1                ; size = w*h = h<<logw
+    movzx          r9d, byte [topq + %2]
+    movd           xm3, r9d
+    vpbroadcastw    m3, xm3
+    movd           xm4, r8d
+    vpbroadcastd    m4, xm4
+    movd           xm5, r6d
+    movd           xm6, r7d
+    movd           xm7, r10d
+    vpbroadcastw    m7, xm7              ; step for m9 = left[h]
+    mova            m9, m7               ; B(y=0) = 1*left[h]
+    mov           r12d, hd
+    dec           r12d
+    movd           xm8, r12d
+    vpbroadcastw    m8, xm8              ; A(y=0) = h-1
+    xor           r11d, r11d
+%endmacro
+
+; Process 16 columns starting at column offset %3.
+;   %1 = (w-1-x) table, %2 = (x+1) table, %3 = column offset, %4 = logw immediate
+%macro PLANAR16 4
+    pmovzxbw         m0, [topq + %3]
+    movu             m1, [%1 + %3*2]
+    movu             m2, [%2 + %3*2]
+    vpmullw          m11, m0, m8
+    vpaddw           m11, m11, m9
+    vpmullw          m12, m1, m10
+    vpmullw           m2, m2, m3
+    vpaddw           m12, m12, m2
+    pmovzxwd         m13, xm11
+    pmovzxwd         m14, xm12
+    vpslld           m13, m13, %4
+    vpslld           m14, m14, xm5
+    vpaddd           m13, m13, m14
+    vpaddd           m13, m13, m4
+    vpsrad           m13, m13, xm6
+    vextracti128     xm11, m11, 1
+    vextracti128     xm12, m12, 1
+    pmovzxwd         m14, xm11
+    pmovzxwd         m15, xm12
+    vpslld           m14, m14, %4
+    vpslld           m15, m15, xm5
+    vpaddd           m14, m14, m15
+    vpaddd           m14, m14, m4
+    vpsrad           m14, m14, xm6
+    packusdw         m13, m13, m14
+    vpermq           m13, m13, 11011000b
+    packuswb         m13, m13, m13
+    vpermq           m13, m13, 11011000b
+    movu   [srcq + %3], xm13
+%endmacro
+
+; Process a narrow row of 4 or 8 columns (offset 0).
+;   %1 = (w-1-x) table, %2 = (x+1) table, %3 = logw immediate, %4 = store instruction
+%macro PLANAR_NARROW 4
+    pmovzxbw          xm0, [topq]
+    movu              xm1, [%1]
+    movu              xm2, [%2]
+    vpmullw           xm11, xm0, xm8
+    vpaddw            xm11, xm11, xm9
+    vpmullw           xm12, xm1, xm10
+    vpmullw           xm2, xm2, xm3
+    vpaddw            xm12, xm12, xm2
+    pmovzxwd          m13, xm11
+    pmovzxwd          m14, xm12
+    vpslld            m13, m13, %3
+    vpslld            m14, m14, xm5
+    vpaddd            m13, m13, m14
+    vpaddd            m13, m13, m4
+    vpsrad            m13, m13, xm6
+    packusdw          m13, m13, m13
+    vpermq            m13, m13, 11011000b
+    packuswb          m13, m13, m13
+    %4      [srcq], xm13
+%endmacro
+
+; void ff_vvc_pred_planar_8_avx2(uint8_t *src, const uint8_t *top,
+;                                const uint8_t *left, int w, int h,
+;                                ptrdiff_t stride);
+cglobal vvc_pred_planar_8, 6, 13, 16, src, top, left, w, h, stride
+    cmp             wd, 16
+    je .w16
+    cmp             wd, 8
+    je .w8
+    cmp             wd, 32
+    je .w32
+    cmp             wd, 64
+    je .w64
+    ; fall through to width 4
+
+.w4:
+    PLANAR_SETUP 2, 4
+.w4_row:
+    ROW_CONSTANTS
+    PLANAR_NARROW pw_wm1x_w4, pw_xp1_w4, 2, movd
+    vpsubw          m8, m8, [pw_1]      ; A -= 1
+    vpaddw          m9, m9, m7          ; B += left[h]
+    add            srcq, strideq
+    inc            r11d
+    cmp            r11d, hd
+    jl .w4_row
+    RET
+
+.w8:
+    PLANAR_SETUP 3, 8
+.w8_row:
+    ROW_CONSTANTS
+    PLANAR_NARROW pw_wm1x_w8, pw_xp1_w8, 3, movq
+    vpsubw          m8, m8, [pw_1]      ; A -= 1
+    vpaddw          m9, m9, m7          ; B += left[h]
+    add            srcq, strideq
+    inc            r11d
+    cmp            r11d, hd
+    jl .w8_row
+    RET
+
+.w16:
+    PLANAR_SETUP 4, 16
+.w16_row:
+    ROW_CONSTANTS
+    PLANAR16 pw_wm1x_w16, pw_xp1_w16, 0, 4
+    vpsubw          m8, m8, [pw_1]      ; A -= 1
+    vpaddw          m9, m9, m7          ; B += left[h]
+    add            srcq, strideq
+    inc            r11d
+    cmp            r11d, hd
+    jl .w16_row
+    RET
+
+.w32:
+    PLANAR_SETUP 5, 32
+.w32_row:
+    ROW_CONSTANTS
+    PLANAR16 pw_wm1x_w32, pw_xp1_w32, 0,  5
+    PLANAR16 pw_wm1x_w32, pw_xp1_w32, 16, 5
+    vpsubw          m8, m8, [pw_1]      ; A -= 1
+    vpaddw          m9, m9, m7          ; B += left[h]
+    add            srcq, strideq
+    inc            r11d
+    cmp            r11d, hd
+    jl .w32_row
+    RET
+
+.w64:
+    PLANAR_SETUP 6, 64
+.w64_row:
+    ROW_CONSTANTS
+    PLANAR16 pw_wm1x_w64, pw_xp1_w64, 0,  6
+    PLANAR16 pw_wm1x_w64, pw_xp1_w64, 16, 6
+    PLANAR16 pw_wm1x_w64, pw_xp1_w64, 32, 6
+    PLANAR16 pw_wm1x_w64, pw_xp1_w64, 48, 6
+    vpsubw          m8, m8, [pw_1]      ; A -= 1
+    vpaddw          m9, m9, m7          ; B += left[h]
+    add            srcq, strideq
+    inc            r11d
+    cmp            r11d, hd
+    jl .w64_row
+    RET
+
+%endif
+%endif
-- 
2.53.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.