[PATCH v2 0/2] VVC: AVX2 planar intra prediction

Gudikandula Samith via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
This series adds an AVX2 implementation of the VVC planar intra
prediction kernel, which currently has no SIMD implementation on any
architecture. Only 8-bit is implemented here; the 10 and 12-bit paths
still use the C reference and can be added in a follow-up.

Patch 1 adds a checkasm test so the second patch can be verified. The
test iterates 8, 10 and 12 bit, so it will also cover the higher depths
once SIMD for them is added.
Patch 2 adds the 8-bit kernel and wires up x86 dispatch.

Tested on AMD Ryzen 7 5825U:
  - checkasm passes: the 8-bit AVX2 kernel matches the C reference
  - fate-vvc passes, unchanged from before the series
  - framemd5 output is identical with and without -cpuflags 0

Changes since v1:
  - checkasm: use separate top0/top1 and left0/left1 buffers so call_ref
    cannot modify call_new's input
  - checkasm: use MIN_TU_SIZE/MAX_TB_SIZE instead of magic 4/64
  - checkasm: use checkasm_check_pixel instead of memcmp/fail
  - asm: compute per-row A and B iteratively (faster; suggested by Frank)
  - asm: index left[h] via r8 and drop r13, saving a mov
  - asm: whitespace/alignment cleanup

Thanks to Frank for the v1 review.

Gudikandula Samith (2):
  tests/checkasm: add vvc_intra test for pred_planar
  lavc/vvc: add AVX2 planar intra prediction

 libavcodec/x86/vvc/Makefile   |   1 +
 libavcodec/x86/vvc/dsp_init.c |  13 ++
 libavcodec/x86/vvc/intra.asm  | 233 ++++++++++++++++++++++++++++++++++
 tests/checkasm/Makefile       |   2 +-
 tests/checkasm/checkasm.c     |   7 +-
 tests/checkasm/checkasm.h     |   1 +
 tests/checkasm/vvc_intra.c    |  89 +++++++++++++
 7 files changed, 342 insertions(+), 4 deletions(-)
 create mode 100644 libavcodec/x86/vvc/intra.asm
 create mode 100644 tests/checkasm/vvc_intra.c

Range-diff against v1:
1:  e222150dee ! 1:  4f0463cfac tests/checkasm: add vvc_intra test for pred_planar
    @@ tests/checkasm/vvc_intra.c (new)
     +
     +static void check_pred_planar(VVCDSPContext *c, const int bit_depth)
     +{
    -+    LOCAL_ALIGNED_32(uint8_t, dst0, [BUF_SIZE]);
    -+    LOCAL_ALIGNED_32(uint8_t, dst1, [BUF_SIZE]);
    -+    LOCAL_ALIGNED_32(uint8_t, top,  [(MAX_TB_SIZE + 1) * 2]);
    -+    LOCAL_ALIGNED_32(uint8_t, left, [(MAX_TB_SIZE + 1) * 2]);
    ++    LOCAL_ALIGNED_32(uint8_t, dst0,  [BUF_SIZE]);
    ++    LOCAL_ALIGNED_32(uint8_t, dst1,  [BUF_SIZE]);
    ++    LOCAL_ALIGNED_32(uint8_t, top0,  [(MAX_TB_SIZE + 1) * 2]);
    ++    LOCAL_ALIGNED_32(uint8_t, top1,  [(MAX_TB_SIZE + 1) * 2]);
    ++    LOCAL_ALIGNED_32(uint8_t, left0, [(MAX_TB_SIZE + 1) * 2]);
    ++    LOCAL_ALIGNED_32(uint8_t, left1, [(MAX_TB_SIZE + 1) * 2]);
     +
     +    const ptrdiff_t stride = BUF_STRIDE / SIZEOF_PIXEL;
     +
     +    declare_func(void, uint8_t *src, const uint8_t *top, const uint8_t *left,
     +                 int w, int h, ptrdiff_t stride);
     +
    -+    randomize_buffers(top,  top,  (MAX_TB_SIZE + 1) * 2);
    -+    randomize_buffers(left, left, (MAX_TB_SIZE + 1) * 2);
    ++    randomize_buffers(top0,  top1,  (MAX_TB_SIZE + 1) * 2);
    ++    randomize_buffers(left0, left1, (MAX_TB_SIZE + 1) * 2);
     +
    -+    for (int h = 4; h <= 64; h <<= 1) {
    -+        for (int w = 4; w <= 64; w <<= 1) {
    ++    for (int h = MIN_TU_SIZE; h <= MAX_TB_SIZE; h <<= 1) {
    ++        for (int w = MIN_TU_SIZE; w <= MAX_TB_SIZE; w <<= 1) {
     +            if (check_func(c->intra.pred_planar,
     +                           "vvc_pred_planar_%dx%d_%d", w, h, bit_depth)) {
     +                memset(dst0, 0, BUF_SIZE);
     +                memset(dst1, 0, BUF_SIZE);
    -+                call_ref(dst0, top, left, w, h, stride);
    -+                call_new(dst1, top, left, w, h, stride);
    -+                if (memcmp(dst0, dst1, BUF_SIZE))
    -+                    fail();
    -+                bench_new(dst1, top, left, w, h, stride);
    ++                call_ref(dst0, top0, left0, w, h, stride);
    ++                call_new(dst1, top1, left1, w, h, stride);
    ++                checkasm_check_pixel(dst0, BUF_STRIDE, dst1, BUF_STRIDE,
    ++                                     w, h, "dst");
    ++                bench_new(dst1, top1, left1, w, h, stride);
     +            }
     +        }
     +    }
2:  43cd7006f7 ! 2:  606ae7a384 lavc/vvc: add AVX2 planar intra prediction
    @@ Commit message
         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)
    +      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/intra.asm (new)
     +                 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
     +
    @@ libavcodec/x86/vvc/intra.asm (new)
     +
     +INIT_YMM avx2
     +
    -+; Per-row scalars, broadcast into vector registers:
    -+; m8 = A = (h-1-y)
    -+; m9 = B = (y+1) * left[h]
    ++; 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
    -+    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
    ++    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
    -+    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]
    -+    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
    ++    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]
    ++    pmovzxbw         m0, [topq + %3]
     +    movu             m1, [%1 + %3*2]
     +    movu             m2, [%2 + %3*2]
     +    vpmullw          m11, m0, m8
    @@ libavcodec/x86/vvc/intra.asm (new)
     +; 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
    ++cglobal vvc_pred_planar_8, 6, 13, 16, src, top, left, w, h, stride
     +    cmp             wd, 16
     +    je .w16
     +    cmp             wd, 8
    @@ libavcodec/x86/vvc/intra.asm (new)
     +.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
    @@ libavcodec/x86/vvc/intra.asm (new)
     +.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
    @@ libavcodec/x86/vvc/intra.asm (new)
     +.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
    @@ libavcodec/x86/vvc/intra.asm (new)
     +    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
    @@ libavcodec/x86/vvc/intra.asm (new)
     +    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

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