diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 2939d75e7..8bbfc1344 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -371,6 +371,37 @@ extern "C" { */ #define SDL_HINT_CAMERA_DRIVER "SDL_CAMERA_DRIVER" +/** + * A variable that limits what CPU features are available. + * + * By default, SDL marks all features the current CPU supports as available. + * This hint allows to limit these to a subset. + * + * When the hint is unset, or empty, SDL will enable all detected CPU + * features. + * + * The variable can be set to a comma separated list containing the following items: + * "all" + * "altivec" + * "sse" + * "sse2" + * "sse3" + * "sse41" + * "sse42" + * "avx" + * "avx2" + * "avx512f" + * "arm-simd" + * "neon" + * "lsx" + * "lasx" + * + * The items can be prefixed by '+'/'-' to add/remove features. + * + * This hint is available since SDL 3.0.0. + */ +#define SDL_HINT_CPU_FEATURE_MASK "SDL_CPU_FEATURE_MASK" + /** * A variable controlling whether DirectInput should be used for controllers * diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c index 0fd3d14bd..49aaf0936 100644 --- a/src/cpuinfo/SDL_cpuinfo.c +++ b/src/cpuinfo/SDL_cpuinfo.c @@ -858,6 +858,80 @@ int SDL_GetCPUCacheLineSize(void) static Uint32 SDL_CPUFeatures = 0xFFFFFFFF; static Uint32 SDL_SIMDAlignment = 0xFFFFFFFF; +static SDL_bool ref_string_equals(const char *ref, const char *test, const char *end_test) { + size_t len_test = end_test - test; + return SDL_strncmp(ref, test, len_test) == 0 && ref[len_test] == '\0' && (test[len_test] == '\0' || test[len_test] == ','); +} + +static Uint32 SDLCALL SDL_CPUFeatureMaskFromHint(void) +{ + Uint32 result_mask = 0xFFFFFFFF; + + const char *hint = SDL_GetHint(SDL_HINT_CPU_FEATURE_MASK); + + if (hint) { + for (const char *spot = hint, *next; *spot; spot = next) { + const char *end = SDL_strchr(spot, ','); + Uint32 spot_mask; + SDL_bool add_spot_mask = SDL_TRUE; + if (end) { + next = end + 1; + } else { + size_t len = SDL_strlen(spot); + end = spot + len; + next = end; + } + if (spot[0] == '+') { + add_spot_mask = SDL_TRUE; + spot += 1; + } else if (spot[0] == '-') { + add_spot_mask = SDL_FALSE; + spot += 1; + } + if (ref_string_equals("all", spot, end)) { + spot_mask = 0xFFFFFFFF; + } else if (ref_string_equals("altivec", spot, end)) { + spot_mask= CPU_HAS_ALTIVEC; + } else if (ref_string_equals("mmx", spot, end)) { + spot_mask = CPU_HAS_MMX; + } else if (ref_string_equals("sse", spot, end)) { + spot_mask = CPU_HAS_SSE; + } else if (ref_string_equals("sse2", spot, end)) { + spot_mask = CPU_HAS_SSE2; + } else if (ref_string_equals("sse3", spot, end)) { + spot_mask = CPU_HAS_SSE3; + } else if (ref_string_equals("sse41", spot, end)) { + spot_mask = CPU_HAS_SSE41; + } else if (ref_string_equals("sse42", spot, end)) { + spot_mask = CPU_HAS_SSE42; + } else if (ref_string_equals("avx", spot, end)) { + spot_mask = CPU_HAS_AVX; + } else if (ref_string_equals("avx2", spot, end)) { + spot_mask = CPU_HAS_AVX2; + } else if (ref_string_equals("avx512f", spot, end)) { + spot_mask = CPU_HAS_AVX512F; + } else if (ref_string_equals("arm-simd", spot, end)) { + spot_mask = CPU_HAS_ARM_SIMD; + } else if (ref_string_equals("neon", spot, end)) { + spot_mask = CPU_HAS_NEON; + } else if (ref_string_equals("lsx", spot, end)) { + spot_mask = CPU_HAS_LSX; + } else if (ref_string_equals("lasx", spot, end)) { + spot_mask = CPU_HAS_LASX; + } else { + /* Ignore unknown/incorrect cpu feature(s) */ + continue; + } + if (add_spot_mask) { + result_mask |= spot_mask; + } else { + result_mask &= ~spot_mask; + } + } + } + return result_mask; +} + static Uint32 SDL_GetCPUFeatures(void) { if (SDL_CPUFeatures == 0xFFFFFFFF) { @@ -920,6 +994,7 @@ static Uint32 SDL_GetCPUFeatures(void) SDL_CPUFeatures |= CPU_HAS_LASX; SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 32); } + SDL_CPUFeatures &= SDL_CPUFeatureMaskFromHint(); } return SDL_CPUFeatures; }