Skip to content

Add NEON for fwd_txfm - #5409

Merged
urvangjoshi merged 5 commits into
AOMediaCodec:mainfrom
jjustiss-apple:jjustiss/neon-fwd-txfm
Sep 18, 2026
Merged

urvangjoshi merged 5 commits into
AOMediaCodec:mainfrom
jjustiss-apple:jjustiss/neon-fwd-txfm

Conversation

@jjustiss-apple

Copy link
Copy Markdown
Contributor

The forward transform pipeline (fwd_txfm) is a significant encoder hotspot
with no NEON specialization. All 1D kernels run scalar C on ARM.

Add NEON intrinsics for the full fwd_txfm pipeline: all 1D kernels
(DCT2, ADST, FDST, IDTX, DDTX, FDDT) for sizes 4-32, matrix-multiply
NEON kernels, a cross-chroma transform (CCTX) NEON path, and
avm_highbd_fdct8x8.

Micro-benchmark results (Apple M3 Pro P-core, 99% CI):

Kernel C (ns) NEON (ns) Speedup
fwd_txfm 4x4 DCT 187 18 10.2x
fwd_txfm 8x8 DCT 209 40 5.2x
fwd_txfm 16x16 DCT 398 189 2.1x
fwd_txfm 32x32 DCT 7326 1266 5.8x
fwd_txfm 8x8 ADST 264 74 3.6x
fwd_txfm 16x16 ADST 1228 405 3.0x
fwd_txfm 16x16 IDTX 323 76 4.2x
cctx 16x16 348 71 4.9x
fdct8x8 88 40 2.2x

CTC results (RA, cpu-used=1, 33 frames, A5):

Metric Delta
Encode time -2.3%
BD-rate Y 0.000%
BD-rate Cb 0.000%
BD-rate Cr 0.000%

Unit tests: FwdTxfmVariantTest (5160 parameterized C-vs-RTCD cases),
HighbdFdct8x8Test (bit-exact + boundary), FwdCctxTest (54 combinations).

@jjustiss-apple

Copy link
Copy Markdown
Contributor Author

Please note, this PR only partially covers the various transform sizes and uses many c-fallbacks still.

Comment thread test/txfm_misc_test.cc Outdated
Comment on lines +116 to +117
const int32x4_t t0 = vaddq_s32(vmulq_s32(cos_t, c1), vmulq_s32(sin_t, c2));
const int32x4_t t1 = vsubq_s32(vmulq_s32(cos_t, c2), vmulq_s32(sin_t, c1));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use fused multiply-accumulate (vmlaq_s32 / vmlsq_s32):
Instead of 4 separate vmulq_s32 + vaddq_s32 + vsubq_s32, you can use vmlaq_s32 (MLA) and vmlsq_s32 (MLS) compute t0 and t1.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. This micro benchmarks out to roughly the same speed as this function is memory bound on my machine.

Comment thread av2/encoder/arm/neon/hybrid_fwd_txfm_neon.c Outdated
vst1q_s32(&src_c1[i], r0);
vst1q_s32(&src_c2[i], r1);
}
for (; i < ncoeffs; i++) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ncoeffs is always multiple of 16, so this block is dead code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another miss on my end, removed.

Comment thread avm_dsp/arm/fwd_txfm_neon.c Outdated
Comment thread avm_dsp/arm/fwd_txfm_neon.c Outdated
Comment on lines +352 to +353
transpose_4x8_s32(res_lo, fin_lo);
transpose_4x8_s32(res_hi, fin_hi);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - reuse transpose_arrays_s32_4x4

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

#include "av2/encoder/fwd_txfm_internal.h"
#include "avm_dsp/txfm_common.h"

static INLINE void transpose_store_4x4_s32(int32x4_t r0, int32x4_t r1,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can reuse transpose_elems_inplace_s32_4x4 function in avm_dsp/arm/transpose_neon.h

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I replaced an additional transpose too that required some more setup, but the disassembly is the same.

Comment thread av2/encoder/arm/neon/highbd_fwd_txfm_neon.c Outdated

@jjustiss-apple jjustiss-apple left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I've addressed all your concerns, Jerome. Thank you for the very thorough review!

Comment thread test/txfm_misc_test.cc Outdated
Comment thread avm_dsp/arm/fwd_txfm_neon.c Outdated
Comment on lines +116 to +117
const int32x4_t t0 = vaddq_s32(vmulq_s32(cos_t, c1), vmulq_s32(sin_t, c2));
const int32x4_t t1 = vsubq_s32(vmulq_s32(cos_t, c2), vmulq_s32(sin_t, c1));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. This micro benchmarks out to roughly the same speed as this function is memory bound on my machine.

Comment thread av2/encoder/arm/neon/hybrid_fwd_txfm_neon.c Outdated
Comment thread av2/encoder/arm/neon/highbd_fwd_txfm_neon.c Outdated
#include "av2/encoder/fwd_txfm_internal.h"
#include "avm_dsp/txfm_common.h"

static INLINE void transpose_store_4x4_s32(int32x4_t r0, int32x4_t r1,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I replaced an additional transpose too that required some more setup, but the disassembly is the same.

vst1q_s32(&src_c1[i], r0);
vst1q_s32(&src_c2[i], r1);
}
for (; i < ncoeffs; i++) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another miss on my end, removed.

Comment thread avm_dsp/arm/fwd_txfm_neon.c Outdated
Comment on lines +352 to +353
transpose_4x8_s32(res_lo, fin_lo);
transpose_4x8_s32(res_hi, fin_hi);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@urvangjoshi
urvangjoshi force-pushed the jjustiss/neon-fwd-txfm branch from e1ab6a4 to 045f6d4 Compare September 18, 2026 18:01
@urvangjoshi
urvangjoshi merged commit 9976f12 into AOMediaCodec:main Sep 18, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants