From 7606c9ac1227bbadd8ee3dbce3b999ae799fc0f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 12 Jun 2025 17:03:19 +0200 Subject: [PATCH] Update minimum CPU arch requirement on Windows to AVX2 with SSE4.2 94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/) --- Source/Engine/Platform/Defines.h | 18 ++-- .../Platform/Windows/WindowsPlatform.cpp | 101 +++++++++++++++--- .../Build/NativeCpp/CompileEnvironment.cs | 47 ++++++++ Source/Tools/Flax.Build/Build/Target.cs | 19 ++++ Source/Tools/Flax.Build/Build/Toolchain.cs | 5 + .../Platforms/Windows/WindowsToolchain.cs | 27 ++++- 6 files changed, 188 insertions(+), 29 deletions(-) diff --git a/Source/Engine/Platform/Defines.h b/Source/Engine/Platform/Defines.h index b5b3274e2..29f64052d 100644 --- a/Source/Engine/Platform/Defines.h +++ b/Source/Engine/Platform/Defines.h @@ -207,28 +207,22 @@ API_ENUM() enum class ArchitectureType #define PLATFORM_UNIX_FAMILY (PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_APPLE_FAMILY) // SIMD defines -#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__) +#if !defined(PLATFORM_SIMD_SSE2) && (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__)) #define PLATFORM_SIMD_SSE2 1 -#if defined(__SSE3__) +#if !defined(PLATFORM_SIMD_SSE3) && (defined(__SSE3__)) #define PLATFORM_SIMD_SSE3 1 #endif -#if defined(__SSE4__) -#define PLATFORM_SIMD_SSE4 1 -#endif -#if defined(__SSE4_1__) +#if !defined(PLATFORM_SIMD_SSE4_1) && (defined(__SSE4_1__)) #define PLATFORM_SIMD_SSE4_1 1 #endif -#if defined(__SSE4_2__) +#if !defined(PLATFORM_SIMD_SSE4_2) && (defined(__SSE4_2__)) #define PLATFORM_SIMD_SSE4_2 1 #endif #endif -#if defined(_M_ARM) || defined(__ARM_NEON__) || defined(__ARM_NEON) +#if !defined(PLATFORM_SIMD_NEON) && (defined(_M_ARM) || defined(__ARM_NEON__) || defined(__ARM_NEON)) #define PLATFORM_SIMD_NEON 1 #endif -#if defined(_M_PPC) || defined(__CELLOS_LV2__) -#define PLATFORM_SIMD_VMX 1 -#endif -#define PLATFORM_SIMD (PLATFORM_SIMD_SSE2 || PLATFORM_SIMD_SSE3 || PLATFORM_SIMD_SSE4 || PLATFORM_SIMD_NEON || PLATFORM_SIMD_VMX) +#define PLATFORM_SIMD (PLATFORM_SIMD_SSE2 || PLATFORM_SIMD_SSE3 || PLATFORM_SIMD_SSE4_1 || PLATFORM_SIMD_SSE4_2 || PLATFORM_SIMD_NEON) // Unicode text macro #if !defined(TEXT) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 272d38cf7..116cb3deb 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -257,6 +257,37 @@ void GetWindowsVersion(String& windowsName, int32& versionMajor, int32& versionM RegCloseKey(hKey); } +#if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 + +struct CPUBrand +{ + char Buffer[0x40]; + + CPUBrand() + { + Buffer[0] = 0; + int32 cpuInfo[4]; + __cpuid(cpuInfo, 0x80000000); + if (cpuInfo[0] >= 0x80000004) + { + // Get name + for (uint32 i = 0; i < 3; i++) + { + __cpuid(cpuInfo, 0x80000002 + i); + memcpy(Buffer + i * sizeof(cpuInfo), cpuInfo, sizeof(cpuInfo)); + } + + // Trim ending whitespaces + int32 size = StringUtils::Length(Buffer); + while (size > 1 && Buffer[size - 1] == ' ') + size--; + Buffer[size] = 0; + } + } +}; + +#endif + LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // Find window to process that message @@ -517,6 +548,60 @@ void WindowsPlatform::PreInit(void* hInstance) ASSERT(hInstance); Instance = hInstance; +#if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 + // Check the minimum vector instruction set support + int32 cpuInfo[4] = { -1 }; + __cpuid(cpuInfo, 0); + int32 cpuInfoSize = cpuInfo[0]; + __cpuid(cpuInfo, 1); + bool SSE2 = cpuInfo[3] & (1u << 26); + bool SSE3 = cpuInfo[2] & (1u << 0); + bool SSE41 = cpuInfo[2] & (1u << 19); + bool SSE42 = cpuInfo[2] & (1u << 20); + bool AVX = cpuInfo[2] & (1u << 28); + bool POPCNT = cpuInfo[2] & (1u << 23); + bool AVX2 = false; + if (cpuInfoSize >= 7) + { + __cpuid(cpuInfo, 7); + AVX2 = cpuInfo[1] & (1u << 5) && (_xgetbv(0) & 6) == 6; + } + const Char* missingFeature = nullptr; +#if defined(__AVX__) + if (!AVX) + missingFeature = TEXT("AVX"); +#endif +#if defined(__AVX2__) + if (!AVX2) + missingFeature = TEXT("AVX2"); +#endif +#if PLATFORM_SIMD_SSE2 + if (!SSE2) + missingFeature = TEXT("SSE2"); +#endif +#if PLATFORM_SIMD_SSE3 + if (!SSE3) + missingFeature = TEXT("SSE3"); +#endif +#if PLATFORM_SIMD_SSE4_1 + if (!SSE41) + missingFeature = TEXT("SSE4.1"); +#endif +#if PLATFORM_SIMD_SSE4_2 + if (!SSE42) + missingFeature = TEXT("SSE4.2"); + if (!POPCNT) + missingFeature = TEXT("POPCNT"); +#endif + if (missingFeature) + { + // Not supported CPU + CPUBrand cpu; + Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer))); + exit(-1); + } +#endif + // Disable the process from being showing "ghosted" while not responding messages during slow tasks DisableProcessWindowsGhosting(); @@ -707,20 +792,8 @@ void WindowsPlatform::LogInfo() #if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 // Log CPU brand - { - char brandBuffer[0x40] = {}; - int32 cpuInfo[4] = { -1 }; - __cpuid(cpuInfo, 0x80000000); - if (cpuInfo[0] >= 0x80000004) - { - for (uint32 i = 0; i < 3; i++) - { - __cpuid(cpuInfo, 0x80000002 + i); - memcpy(brandBuffer + i * sizeof(cpuInfo), cpuInfo, sizeof(cpuInfo)); - } - } - LOG(Info, "CPU: {0}", String(brandBuffer)); - } + CPUBrand cpu; + LOG(Info, "CPU: {0}", String(cpu.Buffer)); #endif LOG(Info, "Microsoft {0} {1}-bit ({2}.{3}.{4})", WindowsName, Platform::Is64BitPlatform() ? TEXT("64") : TEXT("32"), VersionMajor, VersionMinor, VersionBuild); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs index 81c620592..7da495b46 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs @@ -84,6 +84,47 @@ namespace Flax.Build.NativeCpp Latest, } + /// + /// The SIMD architecture to use for code generation. + /// + public enum CpuArchitecture + { + /// + /// No specific architecture set. + /// + None, + + /// + /// Intel Advanced Vector Extensions. + /// + AVX, + + /// + /// Enables Intel Advanced Vector Extensions 2. + /// + AVX2, + + /// + /// Intel Advanced Vector Extensions 512. + /// + AVX512, + + /// + /// Intel Streaming SIMD Extensions 2. + /// + SSE2, + + /// + /// Intel Streaming SIMD Extensions 4.2. + /// + SSE4_2, + + /// + /// ARM Neon. + /// + NEON, + } + /// /// The C++ compilation environment required to build source files in the native modules. /// @@ -104,6 +145,11 @@ namespace Flax.Build.NativeCpp /// public Sanitizer Sanitizers = Sanitizer.None; + /// + /// SIMD architecture to use. + /// + public CpuArchitecture CpuArchitecture = CpuArchitecture.None; + /// /// Enables exceptions support. /// @@ -222,6 +268,7 @@ namespace Flax.Build.NativeCpp CppVersion = CppVersion, FavorSizeOrSpeed = FavorSizeOrSpeed, Sanitizers = Sanitizers, + CpuArchitecture = CpuArchitecture, EnableExceptions = EnableExceptions, RuntimeTypeInfo = RuntimeTypeInfo, Inlining = Inlining, diff --git a/Source/Tools/Flax.Build/Build/Target.cs b/Source/Tools/Flax.Build/Build/Target.cs index 4deecb414..fe3a4e075 100644 --- a/Source/Tools/Flax.Build/Build/Target.cs +++ b/Source/Tools/Flax.Build/Build/Target.cs @@ -248,6 +248,25 @@ namespace Flax.Build Modules.Add("Main"); } + switch (options.CompileEnv.CpuArchitecture) + { + case CpuArchitecture.AVX: + case CpuArchitecture.SSE2: + // Basic SEE2 + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_SSE2=1"); break; + case CpuArchitecture.AVX2: + case CpuArchitecture.SSE4_2: + // Assume full support of SEE4.2 and older + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_SSE2=1"); + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_SSE3=1"); + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_SSE4_1=1"); + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_SSE4_2=1"); + break; + case CpuArchitecture.NEON: + options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_SIMD_NEON=1"); + break; + } + options.CompileEnv.EnableExceptions = true; // TODO: try to disable this! options.CompileEnv.Sanitizers = Configuration.Sanitizers; switch (options.Configuration) diff --git a/Source/Tools/Flax.Build/Build/Toolchain.cs b/Source/Tools/Flax.Build/Build/Toolchain.cs index ca05cecc4..d67766cf8 100644 --- a/Source/Tools/Flax.Build/Build/Toolchain.cs +++ b/Source/Tools/Flax.Build/Build/Toolchain.cs @@ -102,6 +102,11 @@ namespace Flax.Build { options.CompileEnv.IncludePaths.AddRange(SystemIncludePaths); options.LinkEnv.LibraryPaths.AddRange(SystemLibraryPaths); + + if (options.Architecture == TargetArchitecture.x64 || options.Architecture == TargetArchitecture.x86) + options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; + else if (options.Architecture == TargetArchitecture.ARM64 || options.Architecture == TargetArchitecture.ARM) + options.CompileEnv.CpuArchitecture = CpuArchitecture.NEON; } /// diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index 428cc2437..7537cdecf 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -15,6 +15,12 @@ namespace Flax.Build /// [CommandLine("winMinVer", "", "Specifies the minimum Windows version to use (eg. 10).")] public static string WindowsMinVer = "10"; + + /// + /// Specifies the minimum CPU architecture type to support (on x86/x64). + /// + [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (om x86/x64).")] + public static CpuArchitecture WindowsCpuArch = CpuArchitecture.AVX2; // 94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/) } } @@ -80,6 +86,18 @@ namespace Flax.Build.Platforms options.CompileEnv.PreprocessorDefinitions.Add("USE_SOFT_INTRINSICS"); options.LinkEnv.InputLibraries.Add("softintrin.lib"); } + + options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; + if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) + { + // Old Windows had lower support ratio for latest CPU features + options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; + } + if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) + { + // Windows 11 has hard requirement on SSE4.2 + options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + } } /// @@ -87,10 +105,13 @@ namespace Flax.Build.Platforms { base.SetupCompileCppFilesArgs(graph, options, args); - if (Toolset >= WindowsPlatformToolset.v142 && _minVersion.Major >= 11) + switch (options.CompileEnv.CpuArchitecture) { - // Windows 11 requires SSE4.2 - args.Add("/d2archSSE42"); + case CpuArchitecture.AVX: args.Add("/arch:AVX"); break; + case CpuArchitecture.AVX2: args.Add("/arch:AVX2"); break; + case CpuArchitecture.AVX512: args.Add("/arch:AVX512"); break; + case CpuArchitecture.SSE2: args.Add("/arch:SSE2"); break; + case CpuArchitecture.SSE4_2: args.Add("/arch:SSE4.2"); break; } }