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

Frank Plowman via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
On 25/07/2026 22:36, Gudikandula Samith via ffmpeg-devel wrote:
> Implements pred_planar for x86-64 AVX2 at 8 bit.
> 
> Benchmarks on AMD Ryzen 7 5825U:
>   vvc_pred_planar_4x4_8_c:         52.8
>   vvc_pred_planar_4x4_8_avx2:      24.1 (2.16x)
>   vvc_pred_planar_8x8_8_c:        208.3
>   vvc_pred_planar_8x8_8_avx2:      48.3 (4.31x)
>   vvc_pred_planar_16x16_8_c:      778.0
>   vvc_pred_planar_16x16_8_avx2:   160.4 (4.80x)
>   vvc_pred_planar_32x32_8_c:     2356.0
>   vvc_pred_planar_32x32_8_avx2:   580.3 (4.02x)
>   vvc_pred_planar_64x64_8_c:     7414.0
>   vvc_pred_planar_64x64_8_avx2:  2185.9 (3.27x)
> 
> Signed-off-by: Gudikandula Samith <[email protected]>
> ---
>  libavcodec/x86/vvc/Makefile   |   1 +
>  libavcodec/x86/vvc/dsp_init.c |  13 ++
>  libavcodec/x86/vvc/intra.asm  | 224 ++++++++++++++++++++++++++++++++++
>  3 files changed, 238 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..c18c17193a
> --- /dev/null
> +++ b/libavcodec/x86/vvc/intra.asm
> @@ -0,0 +1,224 @@
> +; /*
> +; * 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
> +
> +
> +SECTION .text
> +
> +%if ARCH_X86_64
> +%if HAVE_AVX2_EXTERNAL
> +
> +INIT_YMM avx2
> +
> +; Per-row scalars, broadcast into vector registers:
> +; m8 = A = (h-1-y)
> +; m9 = B = (y+1) * left[h]
> +; m10 = C = left[y]
> +%macro ROW_CONSTANTS 0
> +    mov           eax, r12d
> +    sub           eax, r11d           ;A=(h-1-y)
> +    movd          xm8, eax
> +    vpbroadcastw   m8, xm8
> +    lea            eax, [r11d+1]
> +    imul           eax, r10d          ;B=(y+1)*left[h]
> +    movd           xm9, eax
> +    vpbroadcastw    m9, xm9
> +    movzx           eax, byte [leftq+r11]
> +    movd           xm10, eax
> +    vpbroadcastw   m10, xm10
> +%endmacro

We can get a pretty significant speedup by computing each row's m8 and
m9 iteratively, i.e. in terms of the previous rows:

m8 = m8_prev + 1
m9 = m9_prev + left[h]

where 1 and left[h] can be long-lived vector registers initialized in
PLANAR_SETUP.  In my testing, I got up to a 20% speed improvement in
some sizes on top of your optimizations with this.

> +; Block-constant setup shared by every width.
> +;  %1 = logw (immediate), %2 = top[w] byte offset (=width)
> +%macro PLANAR_SETUP 2
> +    mov            r13d, hd
> +    bsr             r6d, hd
> +    lea             r7d, [r6d +%1 +1]
> +    mov             r8d, hd
> +    shl             r8d, %1          ;size=w*h= h<<logw
> +    movzx           r9d, byte [topq + %2]
> +    movzx          r10d, byte [leftq +r13]

If we move the leftq movzx a few lines earlier, we can remove one of
r8/r13 and save one of the hd movs.

> +    movd            xm3, r9d
> +    vpbroadcastw     m3, xm3
> +    movd            xm4, r8d
> +    vpbroadcastd     m4, xm4
> +    movd            xm5, r6d
> +    movd             xm6, r7d
> +    mov             r12d, hd
> +    dec             r12d
> +    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, 14, 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
> +    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
> +    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
> +    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
> +    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
> +    add            srcq, strideq
> +    inc            r11d
> +    cmp            r11d, hd
> +    jl .w64_row
> +    RET
> +
> +%endif
> +%endif

A general nit: the whitespace in this file is pretty inconsistent.  Our
style is to place the first comma of every line in the same column.
There's also inconsistent use of whitespace around operators.

-- 
Frank

_______________________________________________
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.