From 7afd6c34975c85eef9652a731b1f17d274f99fda Mon Sep 17 00:00:00 2001
From: Gabriel Wang <[EMAIL REDACTED]>
Date: Tue, 7 Jul 2026 22:36:07 +0800
Subject: [PATCH] Add sve2 acceleration for blit2to2 key (#15953)
---
src/video/SDL_blit_N.c | 5 +
src/video/arm/SDL_sve2_blit_A.c | 2 +-
src/video/arm/SDL_sve2_blit_N.c | 272 +++++++++++++++++++++++++++++
src/video/arm/SDL_sve2_blit_N.h | 2 +
src/video/arm/SDL_sve2_extension.h | 36 ++--
src/video/arm/SDL_sve2_swizzle.h | 10 +-
6 files changed, 309 insertions(+), 18 deletions(-)
diff --git a/src/video/SDL_blit_N.c b/src/video/SDL_blit_N.c
index 67ef65bb6cb90..58cf8f58303e8 100644
--- a/src/video/SDL_blit_N.c
+++ b/src/video/SDL_blit_N.c
@@ -3205,6 +3205,11 @@ SDL_BlitFunc SDL_CalculateBlitN(SDL_Surface *surface)
If a particular case turns out to be useful we'll add it. */
if (srcfmt->bytes_per_pixel == 2 && surface->map.identity != 0) {
+#ifdef SDL_SVE2_INTRINSICS
+ if (SDL_HasSVE2()) {
+ return Blit2to2KeySVE2;
+ }
+#endif
return Blit2to2Key;
} else {
#ifdef SDL_ALTIVEC_BLITTERS
diff --git a/src/video/arm/SDL_sve2_blit_A.c b/src/video/arm/SDL_sve2_blit_A.c
index ef4dd5fff52e6..1c4131a98146e 100644
--- a/src/video/arm/SDL_sve2_blit_A.c
+++ b/src/video/arm/SDL_sve2_blit_A.c
@@ -96,7 +96,7 @@ static inline void sdl_sve_rgb565_stride_blend_with_opacity(uint16_t *SDL_RESTRI
size_t uStride,
uint16_t hwOpacity)
{
- sdl_sve_stride_loop_rgb16(uStride, vTailPred)
+ sdl_sve_stride_loop_u16(uStride, vTailPred)
{
svuint16x3_t vSource16x3 =
diff --git a/src/video/arm/SDL_sve2_blit_N.c b/src/video/arm/SDL_sve2_blit_N.c
index c6ae97e53b341..d954e7ecb8405 100644
--- a/src/video/arm/SDL_sve2_blit_N.c
+++ b/src/video/arm/SDL_sve2_blit_N.c
@@ -61,4 +61,276 @@ void Blit8888to565PixelSwizzleSVE2(SDL_BlitInfo *info)
sdl_sve_rgb32_to_rgb565_swizzle_dispatcher(info);
}
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 2)
+static inline void sdl_sve_rgb16_stride_key(uint16_t *SDL_RESTRICT phwSource,
+ uint16_t *SDL_RESTRICT phwTarget,
+ size_t uStride,
+ uint16_t hwKey,
+ uint16_t hwKeyMask)
+{
+ sdl_sve_stride_loop_u16(uStride, vTailPred)
+ {
+ svuint16_t vSource = svld1_u16(vTailPred, phwSource);
+ svuint16_t vTarget = svld1_u16(vTailPred, phwTarget);
+
+ svuint16_t vSourceMasked = svand_n_u16_m(vTailPred,
+ vSource,
+ hwKeyMask);
+ vTarget = svsel_u16(svcmpeq_n_u16(vTailPred, vSourceMasked, hwKey),
+ vTarget,
+ vSource);
+
+ svst1_u16(vTailPred, phwTarget, vTarget);
+
+ phwSource += sve_iteration_advance;
+ phwTarget += sve_iteration_advance;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 3)
+static inline void sdl_sve_rgb16_key(uint8_t *SDL_RESTRICT pchSource,
+ size_t uSourceStride,
+ uint8_t *SDL_RESTRICT pchTarget,
+ size_t uTargetStride,
+ int nWidth,
+ int nHeight,
+ uint16_t hwKey,
+ uint16_t hwKeyMask)
+{
+ assert(0 == ((uintptr_t)pchSource & 0x01));
+ assert(0 == ((uintptr_t)pchTarget & 0x01));
+
+ while (nHeight--) {
+ sdl_sve_rgb16_stride_key((uint16_t *)pchSource,
+ (uint16_t *)pchTarget,
+ nWidth,
+ hwKey,
+ hwKeyMask);
+
+ pchSource += uSourceStride;
+ pchTarget += uTargetStride;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+void Blit2to2KeySVE2(SDL_BlitInfo *info)
+{
+ int width = info->dst_w;
+ int height = info->dst_h;
+ uint8_t *src = info->src;
+ int srcskip = info->src_skip;
+ uint8_t *dst = info->dst;
+ int dstskip = info->dst_skip;
+
+ const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
+ const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
+
+ // Set up some basic variables
+ int srcbpp = srcfmt->bytes_per_pixel;
+ int dstbpp = dstfmt->bytes_per_pixel;
+
+ assert(srcbpp == 2);
+ assert(dstbpp == 2);
+
+ int srcstride = srcskip + srcbpp * width;
+ int dststride = dstskip + dstbpp * width;
+
+ uint32_t ckey = info->colorkey;
+ uint32_t rgbmask = ~srcfmt->Amask;
+ ckey &= rgbmask;
+
+ sdl_sve_rgb16_key(src,
+ srcstride,
+ dst,
+ dststride,
+ width,
+ height,
+ ckey,
+ rgbmask);
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 2)
+static inline void sdl_sve_rgb32_stride_key_copy_alpha(
+ uint32_t *SDL_RESTRICT pwSource,
+ uint32_t *SDL_RESTRICT pwTarget,
+ size_t uStride,
+ uint32_t wKey,
+ uint32_t wKeyMask,
+ uint32_t wOpacifyMask)
+{
+ sdl_sve_stride_loop_u32(uStride, vTailPred)
+ {
+ svuint32_t vSource = svld1_u32(vTailPred, pwSource);
+ svuint32_t vTarget = svld1_u32(vTailPred, pwTarget);
+
+ /*
+ uint32_t opamask = ((uint32_t)info->a) << dstfmt->Ashift;
+ ...
+ if ((*src32 & rgbmask) != ckey) {
+ *dst32 = *src32 | opamask;
+ }
+ */
+ svuint32_t vSourceMasked = svand_n_u32_m(vTailPred,
+ vSource,
+ wKeyMask);
+ vTarget = svsel_u32(svcmpeq_n_u32(vTailPred, vSourceMasked, wKey),
+ vTarget,
+ svorr_n_u32_m(vTailPred, vSource, wOpacifyMask));
+
+ svst1_u32(vTailPred, pwTarget, vTarget);
+
+ pwSource += sve_iteration_advance;
+ pwTarget += sve_iteration_advance;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 2)
+static inline void sdl_sve_rgb32_stride_key_no_alpha(
+ uint32_t *SDL_RESTRICT pwSource,
+ uint32_t *SDL_RESTRICT pwTarget,
+ size_t uStride,
+ uint32_t wKey,
+ uint32_t wKeyMask,
+ uint32_t wActualRGBMask)
+{
+ sdl_sve_stride_loop_u32(uStride, vTailPred)
+ {
+ svuint32_t vSource = svld1_u32(vTailPred, pwSource);
+ svuint32_t vTarget = svld1_u32(vTailPred, pwTarget);
+
+ /*
+ uint32_t rgbmaskactual = srcfmt->Rmask | srcfmt->Gmask | srcfmt->Bmask;
+ ...
+ if ((*src32 & rgbmask) != ckey) {
+ *dst32 = *src32 & rgbmaskactual;
+ }
+ */
+ svuint32_t vSourceMasked = svand_n_u32_m(vTailPred,
+ vSource,
+ wKeyMask);
+ vTarget = svsel_u32(svcmpeq_n_u32(vTailPred, vSourceMasked, wKey),
+ vTarget,
+ svand_n_u32_m(vTailPred, vSource, wActualRGBMask));
+
+ svst1_u32(vTailPred, pwTarget, vTarget);
+
+ pwSource += sve_iteration_advance;
+ pwTarget += sve_iteration_advance;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 3)
+static inline void sdl_sve_rgb32_key_copy_alpha(uint8_t *SDL_RESTRICT pchSource,
+ size_t uSourceStride,
+ uint8_t *SDL_RESTRICT pchTarget,
+ size_t uTargetStride,
+ int nWidth,
+ int nHeight,
+ uint32_t wKey,
+ uint32_t wKeyMask,
+ uint32_t wOpacifyMask)
+{
+ assert(0 == ((uintptr_t)pchSource & 0x03));
+ assert(0 == ((uintptr_t)pchTarget & 0x03));
+
+ while (nHeight--) {
+ sdl_sve_rgb32_stride_key_copy_alpha((uint32_t *)pchSource,
+ (uint32_t *)pchTarget,
+ nWidth,
+ wKey,
+ wKeyMask,
+ wOpacifyMask);
+
+ pchSource += uSourceStride;
+ pchTarget += uTargetStride;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+ARM_NONNULL(1, 3)
+static inline void sdl_sve_rgb32_key_no_alpha(uint8_t *SDL_RESTRICT pchSource,
+ size_t uSourceStride,
+ uint8_t *SDL_RESTRICT pchTarget,
+ size_t uTargetStride,
+ int nWidth,
+ int nHeight,
+ uint32_t wKey,
+ uint32_t wKeyMask,
+ uint32_t wActualRGBMask)
+{
+ assert(0 == ((uintptr_t)pchSource & 0x03));
+ assert(0 == ((uintptr_t)pchTarget & 0x03));
+
+ while (nHeight--) {
+ sdl_sve_rgb32_stride_key_no_alpha((uint32_t *)pchSource,
+ (uint32_t *)pchTarget,
+ nWidth,
+ wKey,
+ wKeyMask,
+ wActualRGBMask);
+
+ pchSource += uSourceStride;
+ pchTarget += uTargetStride;
+ }
+}
+
+SDL_TARGETING("arch=armv8-a+sve2")
+void Blit4to4KeySVE2(SDL_BlitInfo *info)
+{
+ int width = info->dst_w;
+ int height = info->dst_h;
+ uint8_t *src = info->src;
+ int srcskip = info->src_skip;
+ uint8_t *dst = info->dst;
+ int dstskip = info->dst_skip;
+
+ const SDL_PixelFormatDetails *srcfmt = info->src_fmt;
+ const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
+
+ // Set up some basic variables
+ int srcbpp = srcfmt->bytes_per_pixel;
+ int dstbpp = dstfmt->bytes_per_pixel;
+
+ assert(srcbpp == 4);
+ assert(dstbpp == 4);
+
+ int srcstride = srcskip + srcbpp * width;
+ int dststride = dstskip + dstbpp * width;
+
+ uint32_t ckey = info->colorkey;
+ uint32_t rgbmask = ~srcfmt->Amask;
+ ckey &= rgbmask;
+
+ if (dstfmt->Amask) {
+ uint32_t opamask = ((uint32_t)info->a) << dstfmt->Ashift;
+ /* RGBA: set alpha from info->a */
+ sdl_sve_rgb32_key_copy_alpha(src,
+ srcstride,
+ dst,
+ dststride,
+ width,
+ height,
+ ckey,
+ rgbmask,
+ opamask);
+ } else {
+ /* RGBA->RGB: only copy rgb and set alpha to zero */
+ uint32_t rgbmaskactual = srcfmt->Rmask | srcfmt->Gmask | srcfmt->Bmask;
+ sdl_sve_rgb32_key_no_alpha(src,
+ srcstride,
+ dst,
+ dststride,
+ width,
+ height,
+ ckey,
+ rgbmask,
+ rgbmaskactual);
+ }
+}
+
#endif /* SDL_SVE2_INTRINSICS */
\ No newline at end of file
diff --git a/src/video/arm/SDL_sve2_blit_N.h b/src/video/arm/SDL_sve2_blit_N.h
index 3868de0dbb475..16e38deb036f3 100644
--- a/src/video/arm/SDL_sve2_blit_N.h
+++ b/src/video/arm/SDL_sve2_blit_N.h
@@ -29,6 +29,8 @@
void Blit8888to8888PixelSwizzleSVE2(SDL_BlitInfo *info);
void Blit8888to565PixelSwizzleSVE2(SDL_BlitInfo *info);
+void Blit2to2KeySVE2(SDL_BlitInfo *info);
+void Blit4to4KeySVE2(SDL_BlitInfo *info);
#endif /* SDL_SVE2_INTRINSICS */
diff --git a/src/video/arm/SDL_sve2_extension.h b/src/video/arm/SDL_sve2_extension.h
index c18dace028ae2..4e5c67b9c5d68 100644
--- a/src/video/arm/SDL_sve2_extension.h
+++ b/src/video/arm/SDL_sve2_extension.h
@@ -22,7 +22,7 @@
/*
* IMPORTANT: Please do NOT include this header file directly or indirectly
* outside the src/video/arm folder.
- *
+ *
*/
#if !defined(SDL_SVE2_EXTENSION_H) //&& (defined(__ARM_FEATURE_SVE2) && __ARM_FEATURE_SVE2)
@@ -31,15 +31,15 @@
#include "SDL_sve2_util.h"
/*
- * NOTE: Some Android builds didn't attach '-march=armv8-a+sve2' to
+ * NOTE: Some Android builds didn't attach '-march=armv8-a+sve2' to
* SDL_sve2_*.c and hence the macro __ARM_FEATURE_SVE is not
- * defined by the compiler. This might not be a problem as the
+ * defined by the compiler. This might not be a problem as the
* SDL_TARGETING("arch=armv8-a+sve2") enables the feature for
* individual functions, until some version of compilers
- * provides arm_sve.h raising errors then __ARM_FEATURE_SVE
- * is not defined. Although it should be avoided, as a
- * workaround, we have to define the __ARM_FEATURE_SVE here as
- * an ugly hack.
+ * provides arm_sve.h raising errors then __ARM_FEATURE_SVE
+ * is not defined. Although it should be avoided, as a
+ * workaround, we have to define the __ARM_FEATURE_SVE here as
+ * an ugly hack.
*/
#ifdef SDL_PLATFORM_ANDROID
#ifndef __ARM_FEATURE_SVE
@@ -66,12 +66,12 @@
#define svlens32() svlenu32()
#define svlens64() svlenu64()
-#define sdl_sve_stride_loop_accc8888(ma_stride_size, ma_pred_name) \
+#define sdl_sve_stride_loop_pixel(ma_stride_size, ma_pred_name) \
for (svbool_t ma_pred_name, *pTemp = &ma_pred_name; \
pTemp != NULL; \
pTemp = NULL) \
for (size_t SVE_SAFE_NAME(n) = 0, \
- sve_iteration_advance = svlenu32() * 4; \
+ sve_iteration_advance = svlenu8(); \
({ \
ma_pred_name = svwhilelt_b8((int32_t)SVE_SAFE_NAME(n), \
(int32_t)(ma_stride_size)); \
@@ -79,10 +79,9 @@
}); \
SVE_SAFE_NAME(n) += sve_iteration_advance)
-#define sdl_sve_stride_loop_rgb32(ma_stride_size, ma_pred_name) \
- sdl_sve_stride_loop_accc8888(ma_stride_size, ma_pred_name)
+#define sdl_sve_stride_loop_u8 sdl_sve_stride_loop_pixel
-#define sdl_sve_stride_loop_rgb16(ma_stride_size, ma_pred_name) \
+#define sdl_sve_stride_loop_u16(ma_stride_size, ma_pred_name) \
for (svbool_t ma_pred_name, *pTemp = &ma_pred_name; \
pTemp != NULL; \
pTemp = NULL) \
@@ -95,6 +94,19 @@
}); \
SVE_SAFE_NAME(n) += sve_iteration_advance)
+#define sdl_sve_stride_loop_u32(ma_stride_size, ma_pred_name) \
+ for (svbool_t ma_pred_name, *pTemp = &ma_pred_name; \
+ pTemp != NULL; \
+ pTemp = NULL) \
+ for (size_t SVE_SAFE_NAME(n) = 0, \
+ sve_iteration_advance = svlenu32(); \
+ ({ \
+ ma_pred_name = svwhilelt_b16((int32_t)SVE_SAFE_NAME(n), \
+ (int32_t)(ma_stride_size)); \
+ SVE_SAFE_NAME(n) < (ma_stride_size); \
+ }); \
+ SVE_SAFE_NAME(n) += sve_iteration_advance)
+
#define sdl_sve_pixel_ccc_foreach_chn(ma_source_u16x3, \
ma_target_u16x3, \
...) \
diff --git a/src/video/arm/SDL_sve2_swizzle.h b/src/video/arm/SDL_sve2_swizzle.h
index 032e9d6796c4b..f73ebb8e53a1c 100644
--- a/src/video/arm/SDL_sve2_swizzle.h
+++ b/src/video/arm/SDL_sve2_swizzle.h
@@ -22,7 +22,7 @@
/*
* IMPORTANT: Please do NOT include this header file directly or indirectly
* outside the src/video/arm folder.
- *
+ *
*/
#if !defined(SD_SVE2_SWIZZLE_H) //&& (defined(__ARM_FEATURE_SVE2) && __ARM_FEATURE_SVE2)
@@ -31,7 +31,7 @@
#include "SDL_sve2_extension.h"
#define sdl_sve_rgb32_stride_impl(ma_sve_chn_iterator, ...) \
- sdl_sve_stride_loop_rgb32(uStride, vTailPred) \
+ sdl_sve_stride_loop_pixel(uStride, vTailPred) \
{ \
\
svuint16x4_t vSourceLow16x4 = svundef4_u16(); \
@@ -71,7 +71,7 @@
ma_alpha_idx, \
ma_sve_chn_iterator, \
...) \
- sdl_sve_stride_loop_rgb32(uStride, vTailPred) \
+ sdl_sve_stride_loop_pixel(uStride, vTailPred) \
{ \
\
svuint16x4_t vSourceLow16x4 = svundef4_u16(); \
@@ -115,7 +115,7 @@
}
#define sdl_sve_rgb32_to_rgb565_stride_impl(ma_sve_chn_iterator, ...) \
- sdl_sve_stride_loop_rgb32(uStride, vTailPred) \
+ sdl_sve_stride_loop_pixel(uStride, vTailPred) \
{ \
\
svuint16x4_t vSourceLow16x4 = svundef4_u16(); \
@@ -153,7 +153,7 @@
ma_alpha_idx, \
ma_sve_chn_iterator, \
...) \
- sdl_sve_stride_loop_rgb32(uStride, vTailPred) \
+ sdl_sve_stride_loop_pixel(uStride, vTailPred) \
{ \
\
svuint16x4_t vSourceLow16x4 = svundef4_u16(); \