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]