[PATCH v2 2/2] avcodec/x86/h264_intrapred: add AVX2 for 10-bit pred16x16 plane

ZaneHam via ffmpeg-devel <[email protected]> Tue, 28 Jul 2026 12:21:49 +1200
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
From: Zane Hambly <[email protected]>

pred16x16_plane had no SIMD for 10-bit and is the most expensive intra
16x16 mode. This AVX2 version holds the 16 columns as 32-bit
accumulators in two YMM registers, since 10-bit plane values exceed
16-bit range, and fills each row with a clamped linear ramp.

checkasm benchmarks on Ryzen 5 7600X (cycles, lower is better):
                     C       AVX2
pred16x16_plane_10  456.6    50.4  (9.05x)

Signed-off-by: Zane Hambly <[email protected]>
---
 libavcodec/x86/h264_intrapred_10bit.asm | 99 +++++++++++++++++++++++++
 libavcodec/x86/h264_intrapred_init.c    |  2 +
 2 files changed, 101 insertions(+)

diff --git a/libavcodec/x86/h264_intrapred_10bit.asm b/libavcodec/x86/h264_intrapred_10bit.asm
index 78e2f263bc..c2395f824d 100644
--- a/libavcodec/x86/h264_intrapred_10bit.asm
+++ b/libavcodec/x86/h264_intrapred_10bit.asm
@@ -40,6 +40,13 @@ pw_m32101234: dw -3, -2, -1, 0, 1, 2, 3, 4
 pw_m3:        times 8 dw -3
 pd_17:        times 4 dd 17
 
+; plane prediction: H gradient coefficients for top[-1..6] and top[8..15],
+; and the per-column ramp 0..15 (dwords, for the 32-bit accumulators)
+plane_coef_lo: dw -8, -7, -6, -5, -4, -3, -2, -1
+plane_coef_hi: dw  1,  2,  3,  4,  5,  6,  7,  8
+plane_ramp:    dd  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15
+plane_max:     times 16 dw 1023
+
 SECTION .text
 
 ; dest, left, right, src
@@ -1302,4 +1309,96 @@ cglobal pred16x16_128_dc_10, 2, 4
     jg .loop
     RET
 
+;-----------------------------------------------------------------------------
+; void ff_pred16x16_plane_10_avx2(pixel *src, ptrdiff_t stride)
+; H.264 intra 16x16 plane prediction, 10-bit.
+;   H, V = weighted gradients of the top row / left column
+;   pred[x,y] = clip((a + y*V + x*H) >> 5, 0, 1023)
+; Values exceed 16-bit range at 10-bit, so the 16 columns live in two YMM
+; registers of 32-bit accumulators.
+;-----------------------------------------------------------------------------
+INIT_YMM avx2
+cglobal pred16x16_plane_10, 2, 9, 5
+    ; ---- H: gradient over the top row (row -1) ----
+    mov          r2, r0
+    sub          r2, r1                ; r2 = &top[0]
+    movu        xm0, [r2-2]            ; top[-1..6]  (8 words)
+    movu        xm1, [r2+16]           ; top[8..15]  (8 words)
+    pmaddwd     xm0, [plane_coef_lo]
+    pmaddwd     xm1, [plane_coef_hi]
+    paddd       xm0, xm1               ; 4 dword partial sums
+    pshufd      xm1, xm0, 0x0e
+    paddd       xm0, xm1
+    pshufd      xm1, xm0, 0x01
+    paddd       xm0, xm1               ; H (raw) in the low dword
+    movd        r3d, xm0
+    lea         r3d, [r3d*5+32]
+    sar         r3d, 6                 ; r3d = H = (5*H+32)>>6
+    movzx       r8d, word [r2+30]      ; top[15], kept for a
+
+    ; ---- V: gradient over the left column (scalar; it is strided) ----
+    lea          r4, [r0+r1*8]
+    sub          r4, 2                 ; &left[8]
+    lea          r5, [r0+r1*4]
+    lea          r5, [r5+r1*2]
+    sub          r5, 2                 ; &left[6]
+    movzx       r6d, word [r4]
+    movzx       r7d, word [r5]
+    sub         r6d, r7d
+    mov         r2d, r6d               ; V acc, k=1 term
+%assign k 2
+%rep 7
+    add          r4, r1
+    sub          r5, r1
+    movzx       r6d, word [r4]
+    movzx       r7d, word [r5]
+    sub         r6d, r7d
+    imul        r6d, r6d, k
+    add         r2d, r6d
+%assign k k+1
+%endrep
+    lea         r2d, [r2d*5+32]
+    sar         r2d, 6                 ; r2d = V = (5*V+32)>>6
+
+    ; ---- a = 16*(left[15] + top[15] + 1) - 7*(H+V) ----
+    movzx       r6d, word [r4]         ; r4 == &left[15] after the loop
+    lea         r6d, [r6+r8+1]
+    shl         r6d, 4                 ; 16*(left15 + top15 + 1)
+    mov         r7d, r2d
+    add         r7d, r3d               ; H+V
+    mov         r8d, r7d
+    shl         r8d, 3
+    sub         r8d, r7d               ; 7*(H+V)
+    sub         r6d, r8d               ; r6d = a
+
+    ; ---- build the column ramps: acc = a + col*H (dwords) ----
+    movd        xm3, r3d
+    vpbroadcastd m3, xm3               ; H
+    movd        xm2, r6d
+    vpbroadcastd m2, xm2               ; a
+    movd        xm4, r2d
+    vpbroadcastd m4, xm4               ; V
+    pmulld       m0, m3, [plane_ramp]
+    pmulld       m1, m3, [plane_ramp+32]
+    paddd        m0, m2                ; a + {0..7}*H
+    paddd        m1, m2                ; a + {8..15}*H
+
+    ; ---- fill 16 rows ----
+    ; a and H are spent, so m2/m3 are free scratch and the clamp comes from
+    ; memory: the loop stays within m0..m4, no ymm6/7, no Win64 save/restore.
+    mov         r2d, 16
+.loop:
+    vpsrad       m2, m0, 5
+    vpsrad       m3, m1, 5
+    vpackusdw    m2, m2, m3            ; dword->word, clamps low to 0; lanes scrambled
+    vpermq       m2, m2, 0xd8          ; unscramble to columns 0..15
+    vpminuw      m2, m2, [plane_max]   ; clamp high to 1023
+    movu       [r0], m2
+    paddd        m0, m4                ; next row: += V
+    paddd        m1, m4
+    add          r0, r1
+    dec         r2d
+    jg .loop
+    RET
+
 %endif ; HAVE_AVX2_EXTERNAL
diff --git a/libavcodec/x86/h264_intrapred_init.c b/libavcodec/x86/h264_intrapred_init.c
index 852c9ed7d6..d8dbcc1374 100644
--- a/libavcodec/x86/h264_intrapred_init.c
+++ b/libavcodec/x86/h264_intrapred_init.c
@@ -103,6 +103,7 @@ PRED16x16(128_dc, 10, avx2)
 PRED16x16(left_dc, 10, avx2)
 PRED16x16(vertical, 10, avx2)
 PRED16x16(horizontal, 10, avx2)
+PRED16x16(plane, 10, avx2)
 
 /* 8-bit versions */
 PRED16x16(vertical, 8, sse)
@@ -321,6 +322,7 @@ av_cold void ff_h264_pred_init_x86(H264PredContext *h, int codec_id,
             h->pred16x16[LEFT_DC_PRED8x8   ] = ff_pred16x16_left_dc_10_avx2;
             h->pred16x16[VERT_PRED8x8      ] = ff_pred16x16_vertical_10_avx2;
             h->pred16x16[HOR_PRED8x8       ] = ff_pred16x16_horizontal_10_avx2;
+            h->pred16x16[PLANE_PRED8x8     ] = ff_pred16x16_plane_10_avx2;
         }
     }
 }
-- 
2.51.0.windows.2

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