PR #23776 opened by Marcos Ashton (MarcosAsh)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23776
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23776.patch

Adds an x86 SIMD implementation of the 4x4 luma DST-VII inverse transform 
(previously C-only), for 8 and 10 bit, as part of #23022.

The kernel accumulates with pmaddwd on (src0,src2)/(src1,src3) coefficient 
pairs, mirroring the structure of the existing 4x4 IDCT in idct.asm. SSE2 
suffices as a baseline since the kernel is pure pmaddwd/paddd/packssdw; an AVX 
variant is instantiated via the existing macro framework.

checkasm -t hevc_idct passes bit-exact. Benchmarks (Core Ultra 7 155H):
```
hevc_transform_4x4_luma_8_c:     220.8
hevc_transform_4x4_luma_8_sse2:   54.1
hevc_transform_4x4_luma_10_c:    220.7
hevc_transform_4x4_luma_10_sse2:  54.9
```


>From 5306df2f92be9ec25ecb144628403c2eada97010 Mon Sep 17 00:00:00 2001
From: Marcos Ashton Iglesias <[email protected]>
Date: Sat, 11 Jul 2026 17:07:55 +0100
Subject: [PATCH] avcodec/x86/hevc: add SSE2 transform_4x4_luma

Implements the 4x4 luma DST-VII inverse transform for 8 and 10 bit,
using pmaddwd on (src0,src2)/(src1,src3) pairs, mirroring the
existing 4x4 IDCT structure.

checkasm --bench on a Core Ultra 7 155H:
hevc_transform_4x4_luma_8_c:     220.8
hevc_transform_4x4_luma_8_sse2:   54.1
hevc_transform_4x4_luma_10_c:    220.7
hevc_transform_4x4_luma_10_sse2:  54.9

Part of #23022.
---
 libavcodec/x86/hevc/dsp_init.c | 10 +++++
 libavcodec/x86/hevc/idct.asm   | 76 ++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

diff --git a/libavcodec/x86/hevc/dsp_init.c b/libavcodec/x86/hevc/dsp_init.c
index bd967eac67..cee67fb2a6 100644
--- a/libavcodec/x86/hevc/dsp_init.c
+++ b/libavcodec/x86/hevc/dsp_init.c
@@ -87,6 +87,12 @@ void ff_hevc_idct_32x32_10_ ## opt(int16_t *coeffs, int 
col_limit);
 IDCT_FUNCS(sse2)
 IDCT_FUNCS(avx)
 
+#define TRANSFORM_LUMA_FUNCS(opt)                            \
+void ff_hevc_transform_4x4_luma_8_  ## opt(int16_t *coeffs); \
+void ff_hevc_transform_4x4_luma_10_ ## opt(int16_t *coeffs)
+
+TRANSFORM_LUMA_FUNCS(sse2);
+TRANSFORM_LUMA_FUNCS(avx);
 
 #define ff_hevc_pel_filters ff_hevc_qpel_filters
 #define DECL_HV_FILTER(f)                                  \
@@ -839,6 +845,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int 
bit_depth)
 
             c->idct[0]    = ff_hevc_idct_4x4_8_sse2;
             c->idct[1]    = ff_hevc_idct_8x8_8_sse2;
+            c->transform_4x4_luma = ff_hevc_transform_4x4_luma_8_sse2;
 
             c->add_residual[1] = ff_hevc_add_residual_8_8_sse2;
             c->add_residual[2] = ff_hevc_add_residual_16_8_sse2;
@@ -880,6 +887,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int 
bit_depth)
 
             c->idct[0] = ff_hevc_idct_4x4_8_avx;
             c->idct[1] = ff_hevc_idct_8x8_8_avx;
+            c->transform_4x4_luma = ff_hevc_transform_4x4_luma_8_avx;
         }
         if (EXTERNAL_AVX2(cpu_flags)) {
             c->sao_band_filter[0] = ff_hevc_sao_band_filter_8_8_avx2;
@@ -1019,6 +1027,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int 
bit_depth)
 
             c->idct[0]    = ff_hevc_idct_4x4_10_sse2;
             c->idct[1]    = ff_hevc_idct_8x8_10_sse2;
+            c->transform_4x4_luma = ff_hevc_transform_4x4_luma_10_sse2;
 
             c->add_residual[1] = ff_hevc_add_residual_8_10_sse2;
             c->add_residual[2] = ff_hevc_add_residual_16_10_sse2;
@@ -1056,6 +1065,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int 
bit_depth)
 
             c->idct[0] = ff_hevc_idct_4x4_10_avx;
             c->idct[1] = ff_hevc_idct_8x8_10_avx;
+            c->transform_4x4_luma = ff_hevc_transform_4x4_luma_10_avx;
 
             SAO_BAND_INIT(10, avx);
         }
diff --git a/libavcodec/x86/hevc/idct.asm b/libavcodec/x86/hevc/idct.asm
index 088144171d..cfa156c705 100644
--- a/libavcodec/x86/hevc/idct.asm
+++ b/libavcodec/x86/hevc/idct.asm
@@ -35,6 +35,16 @@ pw_64_m64: times 4 dw 64, -64
 pw_83_36: times 4 dw 83, 36
 pw_36_m83: times 4 dw 36, -83
 
+; 4x4 luma (DST-VII) transform coeffs, paired as (src0, src2) and (src1, src3)
+pw_29_84:   times 4 dw  29,  84
+pw_74_55:   times 4 dw  74,  55
+pw_55_m29:  times 4 dw  55, -29
+pw_74_m84:  times 4 dw  74, -84
+pw_74_m74:  times 4 dw  74, -74
+pw_0_74:    times 4 dw   0,  74
+pw_84_55:   times 4 dw  84,  55
+pw_m74_m29: times 4 dw -74, -29
+
 ; 8x8 transform coeffs
 pw_89_75: times 4 dw 89, 75
 pw_50_18: times 4 dw 50, 18
@@ -366,6 +376,71 @@ cglobal hevc_idct_4x4_%1, 1, 1, 5, coeffs
     RET
 %endmacro
 
+; 4x4 luma inverse transform (DST-VII), one pass over 4 columns at once.
+; Unlike the DCT, DST-VII has no even/odd symmetry, so every output is a full
+; dot product of the 4 inputs; we pair them as (src0, src2) and (src1, src3)
+; and accumulate with pmaddwd, then scale, clip16 and transpose for the next 
pass.
+; expects input rows in m0 (rows 0, 1) and m1 (rows 2, 3)
+; %1 - shift
+; %2 - rounding constant (label of a pd_* array)
+%macro TR_4x4_LUMA 2
+    ; m0: (s0, s2) per column     m1: (s1, s3) per column
+    SBUTTERFLY wd, 0, 1, 2
+
+    pmaddwd m2, m0, [pw_29_84]    ; dst0
+    pmaddwd m3, m1, [pw_74_55]
+    paddd   m2, m3
+    pmaddwd m3, m0, [pw_55_m29]   ; dst1
+    pmaddwd m4, m1, [pw_74_m84]
+    paddd   m3, m4
+    pmaddwd m4, m0, [pw_74_m74]   ; dst2
+    pmaddwd m5, m1, [pw_0_74]
+    paddd   m4, m5
+    pmaddwd m5, m0, [pw_84_55]    ; dst3
+    pmaddwd m0, m1, [pw_m74_m29]
+    paddd   m5, m0
+
+    mova  m0, [%2]
+    paddd m2, m0
+    paddd m3, m0
+    paddd m4, m0
+    paddd m5, m0
+
+    psrad m2, %1
+    psrad m3, %1
+    psrad m4, %1
+    psrad m5, %1
+
+    ; clip16
+    packssdw m2, m3              ; dst0 | dst1
+    packssdw m4, m5              ; dst2 | dst3
+
+    ; transpose the 4x4 block for the next pass
+    SBUTTERFLY wd, 2, 4, 0
+    SBUTTERFLY wd, 2, 4, 0
+    SWAP 0, 2
+    SWAP 1, 4
+%endmacro
+
+; void ff_hevc_transform_4x4_luma_{8,10}_<opt>(int16_t *coeffs)
+; %1 = bitdepth
+%macro TRANSFORM_4x4_LUMA 1
+cglobal hevc_transform_4x4_luma_%1, 1, 1, 6, coeffs
+    mova m0, [coeffsq]
+    mova m1, [coeffsq + 16]
+
+    TR_4x4_LUMA 7, pd_64
+%if %1 == 8
+    TR_4x4_LUMA 12, pd_2048
+%elif %1 == 10
+    TR_4x4_LUMA 10, pd_512
+%endif
+
+    mova [coeffsq],      m0
+    mova [coeffsq + 16], m1
+    RET
+%endmacro
+
 ; scale, pack (clip16) and store the residuals     0 e8[0] + o8[0] --> + %1
 ; 4 at one time (4 columns)                        1 e8[1] + o8[1]
 ; from %5: e8/16 + o8/16, with %1 offset                  ...
@@ -832,6 +907,7 @@ INIT_XMM %2
 %endif
 IDCT_8x8 %1
 IDCT_4x4 %1
+TRANSFORM_4x4_LUMA %1
 %endmacro
 
 INIT_IDCT_DC 8
-- 
2.52.0

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

Reply via email to