citra_common: Enable SSE4.2 on x86_64 builds

Enables the use of SSE4.2 instructions on x86_64 CPUs, allowing
compilers to automatically vectorize some loops on citra_common.
A CMake toggle ENABLE_SSE42 (ON by default) has been added
to enable this behaviour.

This change breaks compatibility with CPUs that do not have
SSE4.2 instructions. All modern CPUs (from 2011 onwards) should
always have these instructions. Manual compilation will be
needed for older CPUs.

A message has been added to report if the CPU is incompatible
when starting the emulator.

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
PabloMK7 2025-05-29 22:21:05 +02:00 committed by OpenSauce
parent 9ecd26d2ce
commit 88b3dff278
4 changed files with 73 additions and 0 deletions

View File

@ -105,6 +105,8 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
# Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
@ -126,6 +128,15 @@ if (ENABLE_SDL2_FRONTEND)
add_definitions(-DENABLE_SDL2_FRONTEND)
endif()
if(ENABLE_SSE42 AND (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64"))
message(STATUS "SSE4.2 enabled for x86_64")
if(MSVC)
SET(SSE42_COMPILE_OPTION /arch:SSE4.2)
else()
SET(SSE42_COMPILE_OPTION -msse4.1 -msse4.2)
endif()
endif()
include(CitraHandleSystemLibs)
if (CITRA_USE_PRECOMPILED_HEADERS)

View File

@ -77,6 +77,10 @@ if (CITRA_USE_PRECOMPILED_HEADERS)
target_precompile_headers(citra_meta PRIVATE precompiled_headers.h)
endif()
if (SSE42_COMPILE_OPTION)
target_compile_definitions(citra_meta PRIVATE CITRA_HAS_SSE42)
endif()
# Bundle in-place on MSVC so dependencies can be resolved by builds.
if (ENABLE_QT AND MSVC)
include(BundleTarget)

View File

@ -21,7 +21,60 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}
#endif
#if CITRA_HAS_SSE42
#if defined(_WIN32)
#include <windows.h>
#if defined(_MSC_VER)
#include <intrin.h>
#else
#include <cpuid.h>
#endif // _MSC_VER
#else
#include <cpuid.h>
#endif // _WIN32
static bool CpuSupportsSSE42() {
uint32_t ecx;
#if defined(_MSC_VER)
int cpu_info[4];
__cpuid(cpu_info, 1);
ecx = static_cast<uint32_t>(cpu_info[2]);
#elif defined(__GNUC__) || defined(__clang__)
uint32_t eax, ebx, edx;
if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
return false;
}
#else
#error "Unsupported compiler"
#endif
// Bit 20 of ECX indicates SSE4.2
return (ecx & (1 << 20)) != 0;
}
static bool CheckAndReportSSE42() {
if (!CpuSupportsSSE42()) {
const std::string error_msg =
"This application requires a CPU with SSE4.2 support or higher.\nTo run on unsupported "
"systems, recompile the application with the ENABLE_SSE42 option disabled.";
#if defined(_WIN32)
MessageBoxA(nullptr, error_msg.c_str(), "Incompatible CPU", MB_OK | MB_ICONERROR);
#endif
std::cerr << "Error: " << error_msg << std::endl;
return false;
}
return true;
}
#endif
int main(int argc, char* argv[]) {
#if CITRA_HAS_SSE42
if (!CheckAndReportSSE42()) {
return 1;
}
#endif
#if ENABLE_ROOM
bool launch_room = false;
for (int i = 1; i < argc; i++) {

View File

@ -206,3 +206,8 @@ if (BACKTRACE_LIBRARY AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_CO
target_link_libraries(citra_common PRIVATE ${BACKTRACE_LIBRARY} dl)
target_compile_definitions(citra_common PRIVATE CITRA_LINUX_GCC_BACKTRACE)
endif()
if (SSE42_COMPILE_OPTION)
target_compile_definitions(citra_common PRIVATE CITRA_HAS_SSE42)
target_compile_options(citra_common PRIVATE ${SSE42_COMPILE_OPTION})
endif()