[FFmpeg-devel] [PATCH 2/2] swscale: Neon rgb_to_yuv_half process 16 pixels at a time
Dmitriy Kovalenko
dmtr.kovalenko at outlook.com
Tue May 27 19:58:00 EEST 2025
This patches integrates so called double bufferring when we are loading
2 batch elements at a time and then processing them in parallel. On the
moden arm processors especially Apple Silicon it gives a visible
benefit, for subsampled pixel processing it is especially nice because
it allows to read elements w/ 2 instructions and write with a single one
(which is usually the slowest part).
Including the previous patch in a stack on macbook pro m4 max rgb_to_yuv_half
in checkasm goes up 2x of the c version
---
libswscale/aarch64/input.S | 332 ++++++++++++++++++++++++++++++++++---
1 file changed, 309 insertions(+), 23 deletions(-)
diff --git a/libswscale/aarch64/input.S b/libswscale/aarch64/input.S
index ee8eb24c14..59d66d0022 100644
--- a/libswscale/aarch64/input.S
+++ b/libswscale/aarch64/input.S
@@ -194,40 +194,94 @@ function ff_\fmt_rgb\()ToUV_half_neon, export=1
ldp w12, w13, [x6, #20] // w12: bu, w13: rv
ldp w14, w15, [x6, #28] // w14: gv, w15: bv
4:
- cmp w5, #8
rgb_set_uv_coeff half=1
- b.lt 2f
-1: // load 16 pixels and prefetch memory for the next block
+
+ cmp w5, #16
+ b.lt 2f // Go directly to scalar if < 16
+
+1:
.if \element == 3
- ld3 { v16.16b, v17.16b, v18.16b }, [x3], #48
- prfm pldl1strm, [x3, #48]
+ ld3 { v16.16b, v17.16b, v18.16b }, [x3], #48 // First 16 pixels
+ ld3 { v26.16b, v27.16b, v28.16b }, [x3], #48 // Second 16 pixels
+ prfm pldl1keep, [x3, #96]
.else
- ld4 { v16.16b, v17.16b, v18.16b, v19.16b }, [x3], #64
- prfm pldl1strm, [x3, #64]
+ ld4 { v16.16b, v17.16b, v18.16b, v19.16b }, [x3], #64 // First 16 pixels
+ ld4 { v26.16b, v27.16b, v28.16b, v29.16b }, [x3], #64 // Second 16 pixels
+ prfm pldl1keep, [x3, #128]
.endif
+ // **Sum adjacent pixel pairs**
.if \alpha_first
- uaddlp v21.8h, v19.16b // v21: summed b pairs
- uaddlp v20.8h, v18.16b // v20: summed g pairs
- uaddlp v19.8h, v17.16b // v19: summed r pairs
+ uaddlp v21.8h, v19.16b // Block 1: B sums
+ uaddlp v20.8h, v18.16b // Block 1: G sums
+ uaddlp v19.8h, v17.16b // Block 1: R sums
+ uaddlp v31.8h, v29.16b // Block 2: B sums
+ uaddlp v30.8h, v28.16b // Block 2: G sums
+ uaddlp v29.8h, v27.16b // Block 2: R sums
.else
- uaddlp v19.8h, v16.16b // v19: summed r pairs
- uaddlp v20.8h, v17.16b // v20: summed g pairs
- uaddlp v21.8h, v18.16b // v21: summed b pairs
+ uaddlp v19.8h, v16.16b // Block 1: R sums
+ uaddlp v20.8h, v17.16b // Block 1: G sums
+ uaddlp v21.8h, v18.16b // Block 1: B sums
+ uaddlp v29.8h, v26.16b // Block 2: R sums
+ uaddlp v30.8h, v27.16b // Block 2: G sums
+ uaddlp v31.8h, v28.16b // Block 2: B sums
.endif
- mov v22.16b, v6.16b // U first half
- mov v23.16b, v6.16b // U second half
- mov v24.16b, v6.16b // V first half
- mov v25.16b, v6.16b // V second half
-
- rgb_to_uv_interleaved_product v19, v20, v21, v0, v1, v2, v3, v4, v5, v22, v23, v24, v25, v16, v17, #10
+ // init accumulatos for both blocks
+ mov v7.16b, v6.16b // U_low
+ mov v8.16b, v6.16b // U_high
+ mov v9.16b, v6.16b // V_low
+ mov v10.16b, v6.16b // V_high
+ mov v11.16b, v6.16b // U_low
+ mov v12.16b, v6.16b // U_high
+ mov v13.16b, v6.16b // V_low
+ mov v14.16b, v6.16b // V_high
+
+ smlal v7.4s, v0.4h, v19.4h // U += ru * r (0-3)
+ smlal v9.4s, v3.4h, v19.4h // V += rv * r (0-3)
+ smlal v11.4s, v0.4h, v29.4h // U += ru * r (0-3)
+ smlal v13.4s, v3.4h, v29.4h // V += rv * r (0-3)
+
+ smlal2 v8.4s, v0.8h, v19.8h // U += ru * r (4-7)
+ smlal2 v10.4s, v3.8h, v19.8h // V += rv * r (4-7)
+ smlal2 v12.4s, v0.8h, v29.8h // U += ru * r (4-7)
+ smlal2 v14.4s, v3.8h, v29.8h // V += rv * r (4-7)
+
+ smlal v7.4s, v1.4h, v20.4h // U += gu * g (0-3)
+ smlal v9.4s, v4.4h, v20.4h // V += gv * g (0-3)
+ smlal v11.4s, v1.4h, v30.4h // U += gu * g (0-3)
+ smlal v13.4s, v4.4h, v30.4h // V += gv * g (0-3)
+
+ smlal2 v8.4s, v1.8h, v20.8h // U += gu * g (4-7)
+ smlal2 v10.4s, v4.8h, v20.8h // V += gv * g (4-7)
+ smlal2 v12.4s, v1.8h, v30.8h // U += gu * g (4-7)
+ smlal2 v14.4s, v4.8h, v30.8h // V += gv * g (4-7)
+
+ smlal v7.4s, v2.4h, v21.4h // U += bu * b (0-3)
+ smlal v9.4s, v5.4h, v21.4h // V += bv * b (0-3)
+ smlal v11.4s, v2.4h, v31.4h // U += bu * b (0-3)
+ smlal v13.4s, v5.4h, v31.4h // V += bv * b (0-3)
+
+ smlal2 v8.4s, v2.8h, v21.8h // U += bu * b (4-7)
+ smlal2 v10.4s, v5.8h, v21.8h // V += bv * b (4-7)
+ smlal2 v12.4s, v2.8h, v31.8h // U += bu * b (4-7)
+ smlal2 v14.4s, v5.8h, v31.8h // V += bv * b (4-7)
+
+ sqshrn v16.4h, v7.4s, #10 // U (0-3)
+ sqshrn v17.4h, v9.4s, #10 // V (0-3)
+ sqshrn v22.4h, v11.4s, #10 // U (0-3)
+ sqshrn v23.4h, v13.4s, #10 // V (0-3)
+
+ sqshrn2 v16.8h, v8.4s, #10 // U (0-7)
+ sqshrn2 v17.8h, v10.4s, #10 // V (0-7)
+ sqshrn2 v22.8h, v12.4s, #10 // U (0-7)
+ sqshrn2 v23.8h, v14.4s, #10 // V (0-7)
- str q16, [x0], #16 // store dst_u
- str q17, [x1], #16 // store dst_v
+ stp q16, q22, [x0], #32 // Store all 16 U values
+ stp q17, q23, [x1], #32 // Store all 16 V values
- sub w5, w5, #8 // width -= 8
- cmp w5, #8 // width >= 8 ?
+ sub w5, w5, #16 // width -= 16
+ cmp w5, #16 // width >= 16 ?
b.ge 1b
cbz w5, 3f // No pixels left? Exit
@@ -459,3 +513,235 @@ endfunc
DISABLE_DOTPROD
#endif
+
+.macro rgbToUV_half_neon_double fmt_bgr, fmt_rgb, element, alpha_first=0
+function ff_\fmt_bgr\()ToUV_half_neon_double, export=1
+ cbz w5, 9f // exit immediately if width is 0
+ cmp w5, #16 // check if we have at least 16 pixels
+ b.lt _ff_\fmt_bgr\()ToUV_half_neon
+
+ ldp w12, w11, [x6, #12] // w12: bu, w11: gu
+ ldp w10, w15, [x6, #20] // w10: ru, w15: bv
+ ldp w14, w13, [x6, #28] // w14: gv, w13: rv
+
+ b .Lcommon_uv_processing_\fmt_bgr
+endfunc
+
+function ff_\fmt_rgb\()ToUV_half_neon_double, export=1
+ cbz w5, 9f // exit immediately if width is 0
+ cmp w5, #16 // check if we have at least 16 pixels
+ b.lt _ff_\fmt_rgb\()ToUV_half_neon
+
+ ldp w10, w11, [x6, #12] // w10: ru, w11: gu
+ ldp w12, w13, [x6, #20] // w12: bu, w13: rv
+ ldp w14, w15, [x6, #28] // w14: gv, w15: bv
+
+.Lcommon_uv_processing_\fmt_bgr:
+ rgb_set_uv_coeff half=1
+
+ zip1 v7.8h, v0.8h, v3.8h // [ru0, rv0, ru1, rv1, ru2, rv2, ru3, rv3]
+ zip2 v8.8h, v0.8h, v3.8h // [ru4, rv4, ru5, rv5, ru6, rv6, ru7, rv7]
+ zip1 v9.8h, v1.8h, v4.8h // [gu0, gv0, gu1, gv1, gu2, gv2, gu3, gv3]
+ zip2 v10.8h, v1.8h, v4.8h // [gu4, gv4, gu5, gv5, gu6, gv6, gu7, gv7]
+ zip1 v11.8h, v2.8h, v5.8h // [bu0, bv0, bu1, bv1, bu2, bv2, bu3, bv3]
+ zip2 v12.8h, v2.8h, v5.8h // [bu4, bv4, bu5, bv5, bu6, bv6, bu7, bv7]
+
+ zip1 v13.4s, v6.4s, v6.4s // [const, const, const, const] for U/V pairs
+ zip2 v14.4s, v6.4s, v6.4s // [const, const, const, const] for U/V pairs
+
+.Lprocess_16_\fmt_bgr:
+ // **OPTIMIZED LOADING: Load and sum with immediate interleaving**
+ .if \element == 3
+ ld3 { v16.16b, v17.16b, v18.16b }, [x3], #48
+ ld3 { v26.16b, v27.16b, v28.16b }, [x3], #48
+ prfm pldl1keep, [x3, #96] // Early prefetch
+
+ // Sum and immediately create interleaved RGB data
+ uaddlp v19.8h, v16.16b
+ uaddlp v20.8h, v17.16b
+ uaddlp v21.8h, v18.16b
+ uaddlp v29.8h, v26.16b
+ uaddlp v30.8h, v27.16b
+ uaddlp v31.8h, v28.16b
+ .else
+ ld4 { v16.16b, v17.16b, v18.16b, v19.16b }, [x3], #64
+ ld4 { v26.16b, v27.16b, v28.16b, v29.16b }, [x3], #64
+ prfm pldl1keep, [x3, #128] // Early prefetch
+
+ .if \alpha_first
+ uaddlp v21.8h, v19.16b
+ uaddlp v20.8h, v18.16b
+ uaddlp v19.8h, v17.16b
+ uaddlp v31.8h, v29.16b
+ uaddlp v30.8h, v28.16b
+ uaddlp v29.8h, v27.16b
+ .else
+ uaddlp v19.8h, v16.16b
+ uaddlp v20.8h, v17.16b
+ uaddlp v21.8h, v18.16b
+ uaddlp v29.8h, v26.16b
+ uaddlp v30.8h, v27.16b
+ uaddlp v31.8h, v28.16b
+ .endif
+ .endif
+
+ zip1 v22.8h, v19.8h, v19.8h // [r0, r0, r1, r1, r2, r2, r3, r3] Block 1
+ zip2 v23.8h, v19.8h, v19.8h // [r4, r4, r5, r5, r6, r6, r7, r7] Block 1
+ zip1 v24.8h, v20.8h, v20.8h // [g0, g0, g1, g1, g2, g2, g3, g3] Block 1
+ zip2 v25.8h, v20.8h, v20.8h // [g4, g4, g5, g5, g6, g6, g7, g7] Block 1
+
+ mov v0.16b, v13.16b // UV accumulator 1a
+ mov v1.16b, v13.16b // UV accumulator 1b
+ mov v2.16b, v14.16b // UV accumulator 1c
+ mov v3.16b, v14.16b // UV accumulator 1d
+
+ smlal v0.4s, v7.4h, v22.4h
+ smlal2 v1.4s, v7.8h, v22.8h
+ smlal v2.4s, v8.4h, v23.4h
+ smlal2 v3.4s, v8.8h, v23.8h
+
+ smlal v0.4s, v9.4h, v24.4h
+ smlal2 v1.4s, v9.8h, v24.8h
+ smlal v2.4s, v10.4h, v25.4h
+ smlal2 v3.4s, v10.8h, v25.8h
+
+ zip1 v22.8h, v21.8h, v21.8h // [b0, b0, b1, b1, b2, b2, b3, b3] Block 1
+ zip2 v23.8h, v21.8h, v21.8h // [b4, b4, b5, b5, b6, b6, b7, b7] Block 1
+
+ smlal2 v1.4s, v11.8h, v22.8h
+ smlal v2.4s, v12.4h, v23.4h
+ smlal2 v3.4s, v12.8h, v23.8h
+
+ zip1 v22.8h, v29.8h, v29.8h // [r0, r0, r1, r1, r2, r2, r3, r3] Block 2
+ zip2 v23.8h, v29.8h, v29.8h // [r4, r4, r5, r5, r6, r6, r7, r7] Block 2
+ zip1 v24.8h, v30.8h, v30.8h // [g0, g0, g1, g1, g2, g2, g3, g3] Block 2
+ zip2 v25.8h, v30.8h, v30.8h // [g4, g4, g5, g5, g6, g6, g7, g7] Block 2
+
+ mov v4.16b, v13.16b
+ mov v5.16b, v13.16b
+ mov v6.16b, v14.16b
+ mov v16.16b, v14.16b
+
+ smlal v4.4s, v7.4h, v22.4h
+ smlal2 v5.4s, v7.8h, v22.8h
+ smlal v6.4s, v8.4h, v23.4h
+ smlal2 v16.4s, v8.8h, v23.8h
+
+ smlal v4.4s, v9.4h, v24.4h
+ smlal2 v5.4s, v9.8h, v24.8h
+ smlal v6.4s, v10.4h, v25.4h
+ smlal2 v16.4s, v10.8h, v25.8h
+
+ zip1 v22.8h, v31.8h, v31.8h // [b0, b0, b1, b1, b2, b2, b3, b3] Block 2
+ zip2 v23.8h, v31.8h, v31.8h // [b4, b4, b5, b5, b6, b6, b7, b7] Block 2
+
+ smlal v4.4s, v11.4h, v22.4h // Process U&V for b0,b1 simultaneously
+ smlal2 v5.4s, v11.8h, v22.8h // Process U&V for b2,b3 simultaneously
+ smlal v6.4s, v12.4h, v23.4h // Process U&V for b4,b5 simultaneously
+ smlal2 v16.4s, v12.8h, v23.8h // Process U&V for b6,b7 simultaneously
+
+ uzp1 v17.4s, v0.4s, v1.4s
+ uzp2 v18.4s, v0.4s, v1.4s
+ uzp1 v19.4s, v2.4s, v3.4s
+ uzp2 v20.4s, v2.4s, v3.4s
+
+ uzp1 v21.4s, v4.4s, v5.4s // Extract U values (Block 2, part 1)
+ uzp2 v22.4s, v4.4s, v5.4s // Extract V values (Block 2, part 1)
+ uzp1 v23.4s, v6.4s, v16.4s // Extract U values (Block 2, part 2)
+ uzp2 v24.4s, v6.4s, v16.4s // Extract V values (Block 2, part 2)
+
+ sqshrn v25.4h, v17.4s, #10 // U1 (first 4)
+ sqshrn2 v25.8h, v19.4s, #10 // U1 (complete 8)
+ sqshrn v26.4h, v18.4s, #10 // V1 (first 4)
+ sqshrn2 v26.8h, v20.4s, #10 // V1 (complete 8)
+
+ sqshrn v27.4h, v21.4s, #10 // U2 (first 4)
+ sqshrn2 v27.8h, v23.4s, #10 // U2 (complete 8)
+ sqshrn v28.4h, v22.4s, #10 // V2 (first 4)
+ sqshrn2 v28.8h, v24.4s, #10 // V2 (complete 8)
+
+ // **EFFICIENT STORE: Use STP for optimal memory bandwidth**
+ stp q25, q27, [x0], #32 // Store U1, U2
+ stp q26, q28, [x1], #32 // Store V1, V2
+
+ sub w5, w5, #16 // Decrement counter
+ cmp w5, #16 // Check for more blocks
+ b.ge .Lprocess_16_\fmt_bgr
+
+ // Standard 8-pixel and scalar fallbacks (unchanged)
+ cmp w5, #8
+ b.lt .Lscalar_loop_init_\fmt_bgr
+
+.Lprocess_8_\fmt_bgr:
+ .if \element == 3
+ ld3 { v16.16b, v17.16b, v18.16b }, [x3], #48
+ uaddlp v19.8h, v16.16b
+ uaddlp v20.8h, v17.16b
+ uaddlp v21.8h, v18.16b
+ .else
+ ld4 { v16.16b, v17.16b, v18.16b, v19.16b }, [x3], #64
+ .if \alpha_first
+ uaddlp v21.8h, v19.16b
+ uaddlp v20.8h, v18.16b
+ uaddlp v19.8h, v17.16b
+ .else
+ uaddlp v19.8h, v16.16b
+ uaddlp v20.8h, v17.16b
+ uaddlp v21.8h, v18.16b
+ .endif
+ .endif
+
+ rgb_set_uv_coeff half=1
+ mov v22.16b, v6.16b
+ mov v23.16b, v6.16b
+ mov v24.16b, v6.16b
+ mov v25.16b, v6.16b
+
+ rgb_to_uv_interleaved_product v19, v20, v21, v0, v1, v2, v3, v4, v5, v22, v23, v24, v25, v16, v17, #10
+
+ str q16, [x0], #16
+ str q17, [x1], #16
+
+ sub w5, w5, #8
+ cmp w5, #8
+ b.ge .Lprocess_8_\fmt_bgr
+
+.Lscalar_loop_init_\fmt_bgr:
+ cbz w5, 9f
+
+.Lscalar_loop_\fmt_bgr:
+ .if \alpha_first
+ rgb_load_add_half 1, 5, 2, 6, 3, 7
+ .else
+ .if \element == 3
+ rgb_load_add_half 0, 3, 1, 4, 2, 5
+ .else
+ rgb_load_add_half 0, 4, 1, 5, 2, 6
+ .endif
+ .endif
+
+ mov x8, x9
+ mov x17, x9
+ smaddl x8, w2, w10, x8
+ smaddl x17, w2, w13, x17
+ smaddl x8, w4, w11, x8
+ smaddl x17, w4, w14, x17
+ smaddl x8, w7, w12, x8
+ smaddl x17, w7, w15, x17
+ asr w8, w8, #10
+ asr w17, w17, #10
+
+ strh w8, [x0], #2
+ strh w17, [x1], #2
+
+ sub w5, w5, #2
+ add x3, x3, #(2*\element)
+ cbnz w5, .Lscalar_loop_\fmt_bgr
+
+9: ret
+endfunc
+.endm
+
+rgbToUV_half_neon_double bgra32, rgba32, element=4
+
+rgbToUV_half_neon_double abgr32, argb32, element=4, alpha_first=1
--
2.49.0
More information about the ffmpeg-devel
mailing list