From baea81efffc352cf7559a909505e8aab8e1d9cc0 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 9 Nov 2024 11:59:24 -0500 Subject: [PATCH] macos: Add support for OpenGOAL on Apple Silicon via Rosetta 2 on Sequoia+ --- CMakeLists.txt | 9 ++++++++- CMakePresets.json | 11 +++++++++++ Taskfile.yml | 8 ++++++++ common/util/os.cpp | 25 +++++++++++++++++++++++++ common/util/os.h | 2 ++ game/CMakeLists.txt | 5 ++++- game/main.cpp | 12 ++++++++++++ 7 files changed, 70 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd099d7888..ba072d6518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ set(CMAKE_CXX_STANDARD 20) project(jak) include(CTest) +# Options +option(BUILD_X86_ON_MACOS "Builds the project for macOS for Rosetta 2" OFF) + # Include third-party modules set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/third-party/cmake/modules/) @@ -119,7 +122,11 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # pin to AVX for macOS, hopefully all macOS runners have atleast this architecture # technically speaking, SSE4 is the cutoff for Apple Silicon so...only a matter of time! - if(NOT CMAKE_CXX_COMPILER_TARGET STREQUAL "arm64-apple-darwin") + if (BUILD_X86_ON_MACOS) + message("Targetting x86 on macOS via Rosetta 2 (requires Sequoia)") + set(CMAKE_OSX_ARCHITECTURES "x86_64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx") + elseif(NOT CMAKE_CXX_COMPILER_TARGET STREQUAL "arm64-apple-darwin") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx") endif() diff --git a/CMakePresets.json b/CMakePresets.json index 1fe236756b..ee5a09198c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -174,6 +174,17 @@ "ZYDIS_BUILD_SHARED_LIB": "OFF" } }, + { + "name": "Release-macos-rosetta-clang-static", + "displayName": "MacOS Rosetta Static Release (clang)", + "description": "Build with Clang as Release without Debug Symbols but statically linked, requires MacOS Sequoia", + "inherits": ["base-macos-release", "base-clang"], + "cacheVariables": { + "BUILD_X86_ON_MACOS": "ON", + "STATICALLY_LINK": "true", + "ZYDIS_BUILD_SHARED_LIB": "OFF" + } + }, { "name": "Release-linux-clang-asan", "displayName": "Linux Release (clang-asan)", diff --git a/Taskfile.yml b/Taskfile.yml index f691c677de..c55e2c8e2e 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -67,6 +67,14 @@ tasks: cmds: - "{{.GK_BIN_RELEASE_DIR}}/gk -v --game {{.GAME}} -- -fakeiso -debug" # DEVELOPMENT + gen-cmake: + desc: "Generate the CMake" + cmds: + - "cmake -B build --preset={{.CMAKE_PRESET}}" + build: + desc: "Build the project using the generated CMake" + cmds: + - "cmake --build build --parallel {{.CMAKE_NUM_THREADS}}" repl: desc: "Start the REPL" preconditions: diff --git a/common/util/os.cpp b/common/util/os.cpp index 397a0b62fd..393c6e7789 100644 --- a/common/util/os.cpp +++ b/common/util/os.cpp @@ -3,6 +3,12 @@ #include "common/common_types.h" #include "common/log/log.h" +#ifdef __APPLE__ +#include +#include +#include +#endif + #ifdef _WIN32 // clang-format off #define NOMINMAX @@ -104,3 +110,22 @@ void setup_cpu_info() { CpuInfo& get_cpu_info() { return gCpuInfo; } + +std::optional get_macos_version() { + #ifndef __APPLE__ + return {}; + #endif + char buffer[128]; + size_t bufferlen = 128; + auto ok = sysctlbyname("kern.osproductversion",&buffer,&bufferlen,NULL,0); + if (ok != 0) { + lg::warn("Unable to check for `kern.osproductversion` to determine macOS version"); + return {}; + } + try { + return std::stod(buffer); + } catch (std::exception e) { + lg::error("Error occured when attempting to convert sysctl value {} to number", buffer); + return {}; + } +} \ No newline at end of file diff --git a/common/util/os.h b/common/util/os.h index de779ee67e..37e7991114 100644 --- a/common/util/os.h +++ b/common/util/os.h @@ -16,3 +16,5 @@ struct CpuInfo { }; CpuInfo& get_cpu_info(); + +std::optional get_macos_version(); diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index df32b480ad..d4bada0a36 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -1,7 +1,10 @@ # Set a more convenient ARM flag -if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND (NOT BUILD_X86_ON_MACOS)) set(ARM64_ARCH TRUE) message(STATUS "ARM64 architecture detected") +elseif() + set(ARM64_ARCH FALSE) + message(STATUS "Forcefully targetting x86 via Rosetta 2") else() set(ARM64_ARCH FALSE) message(STATUS "Non-ARM64 architecture detected") diff --git a/game/main.cpp b/game/main.cpp index 32e982783c..15d5f1655e 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -179,10 +179,22 @@ int main(int argc, char** argv) { setup_cpu_info(); // If the CPU doesn't have AVX, GOAL code won't work and we exit. if (!get_cpu_info().has_avx) { + // Check if we are on a modern enough version of macOS so that AVX can be + // emulated via rosetta + #ifdef __APPLE__ + auto macos_version = get_macos_version(); + if (macos_version < 15.0) { + lg::info("Your CPU does not support AVX. But the newer version of Rosetta supports it, update to atleast Sequoia to run OpenGOAL!"); + dialogs::create_error_message_dialog( + "Unmet Requirements", "Your CPU does not support AVX. But the newer version of Rosetta supports it, update to atleast Sequoia to run OpenGOAL!"); + return -1; + } + #else lg::info("Your CPU does not support AVX, which is required for OpenGOAL."); dialogs::create_error_message_dialog( "Unmet Requirements", "Your CPU does not support AVX, which is required for OpenGOAL."); return -1; + #endif } // set up file paths for resources. This is the full repository when developing, and the data