From 6e2de8ba46094075bd7fcf54ea37511b1c0035bd Mon Sep 17 00:00:00 2001 From: Paul Barrass Date: Tue, 16 Jun 2026 19:19:48 +0100 Subject: [PATCH 1/2] Enable native arm64 macOS build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bundled deps/osx libraries are x86_64-only and the macOS port had bit-rotted against modern Apple Clang. These changes let the engine, tools, and editor build and run natively on Apple Silicon. - byte_serializer: add long/unsigned long overloads on Apple — LP64 makes size_t distinct from uint64_t (which is long long there), so otherwise serializing a size_t is ambiguous/unmatched - os_mac: parseProgramPath returns the full executable path so the caller's parentPath() lands on the right asset dir (was one level too high) - opengl: guard glObjectLabel calls — it's null on macOS GL 4.1 (no KHR_debug) - zlib/zutil: don't define the classic-Mac fdopen stub on modern macOS, where TARGET_OS_MAC is always 1 - avf: update MovieAPI/MoviePlayer signatures to take const HalleyAPI&, matching the current base interface - shader_importer_dxc: guard D3D12-only code behind _MSC_VER - vector_test: add typename on dependent types (Apple Clang requires it) - cmake: link Security + CoreFoundation (needed for httplib TLS) on Apple --- cmake/HalleyProject.cmake | 6 ++++-- src/contrib/zlib/zutil.h | 2 +- .../core/include/halley/bytes/byte_serializer.h | 10 ++++++++++ src/engine/core/src/os/os_mac.cpp | 11 +++++++---- src/plugins/avf/src/avf_movie_api.h | 2 +- src/plugins/avf/src/avf_movie_api.mm | 4 ++-- src/plugins/avf/src/avf_movie_player.h | 3 ++- src/plugins/avf/src/avf_movie_player.mm | 4 ++-- src/plugins/opengl/src/shader_opengl.cpp | 8 ++++++-- src/plugins/opengl/src/texture_opengl.cpp | 4 +++- src/tests/src/vector_test.cpp | 8 ++++---- .../src/assets/importers/shader_importer_dxc.cpp | 5 ++++- 12 files changed, 46 insertions(+), 21 deletions(-) diff --git a/cmake/HalleyProject.cmake b/cmake/HalleyProject.cmake index 8e05ce4c7a..ad68d70dab 100644 --- a/cmake/HalleyProject.cmake +++ b/cmake/HalleyProject.cmake @@ -351,10 +351,12 @@ if (APPLE) find_library(IOKIT_LIBRARY IOKit) find_library(COREVIDEO_LIBRARY CoreVideo) find_library(AUDIOTOOLBOX_LIBRARY AudioToolbox) + find_library(SECURITY_LIBRARY Security) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) - mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY COREAUDIO_LIBRARY AUDIOTOOLBOX_LIBRARY AUDIOUNIT_LIBRARY FORCEFEEDBACK_LIBRARY IOKIT_LIBRARY COREVIDEO_LIBRARY) + mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY COREAUDIO_LIBRARY AUDIOTOOLBOX_LIBRARY AUDIOUNIT_LIBRARY FORCEFEEDBACK_LIBRARY IOKIT_LIBRARY COREVIDEO_LIBRARY SECURITY_LIBRARY COREFOUNDATION_LIBRARY) - set(EXTRA_LIBS ${EXTRA_LIBS} ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${COREAUDIO_LIBRARY} ${AUDIOTOOLBOX_LIBRARY} ${AUDIOUNIT_LIBRARY} ${FORCEFEEDBACK_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY} iconv) + set(EXTRA_LIBS ${EXTRA_LIBS} ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${COREAUDIO_LIBRARY} ${AUDIOTOOLBOX_LIBRARY} ${AUDIOUNIT_LIBRARY} ${FORCEFEEDBACK_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY} ${SECURITY_LIBRARY} ${COREFOUNDATION_LIBRARY} iconv) if (BUILD_MACOSX_BUNDLE) add_definitions(-DHALLEY_MACOSX_BUNDLE) diff --git a/src/contrib/zlib/zutil.h b/src/contrib/zlib/zutil.h index 0bc7f4ecd1..c86c2580fe 100644 --- a/src/contrib/zlib/zutil.h +++ b/src/contrib/zlib/zutil.h @@ -137,7 +137,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#if defined(MACOS) || defined(TARGET_OS_MAC) +#if defined(MACOS) /* classic Mac OS only; modern macOS always defines TARGET_OS_MAC=1, which must not trigger the fdopen stub below */ # define OS_CODE 7 # ifndef Z_SOLO # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os diff --git a/src/engine/core/include/halley/bytes/byte_serializer.h b/src/engine/core/include/halley/bytes/byte_serializer.h index 72110b1405..029d0e2bee 100644 --- a/src/engine/core/include/halley/bytes/byte_serializer.h +++ b/src/engine/core/include/halley/bytes/byte_serializer.h @@ -118,6 +118,11 @@ namespace Halley { Serializer& operator<<(uint32_t val) { return serializeInteger(val); } Serializer& operator<<(int64_t val) { return serializeInteger(val); } Serializer& operator<<(uint64_t val) { return serializeInteger(val); } +#if defined(__APPLE__) + // On Apple LP64, int64_t/uint64_t are long long, leaving long/unsigned long (e.g. size_t) unmatched + Serializer& operator<<(long val) { return serializeInteger(static_cast(val)); } + Serializer& operator<<(unsigned long val) { return serializeInteger(static_cast(val)); } +#endif Serializer& operator<<(float val) { return serializePod(val); } Serializer& operator<<(double val) { return serializePod(val); } @@ -387,6 +392,11 @@ namespace Halley { Deserializer& operator>>(uint32_t& val) { return deserializeInteger(val); } Deserializer& operator>>(int64_t& val) { return deserializeInteger(val); } Deserializer& operator>>(uint64_t& val) { return deserializeInteger(val); } +#if defined(__APPLE__) + // On Apple LP64, int64_t/uint64_t are long long, leaving long/unsigned long (e.g. size_t) unmatched + Deserializer& operator>>(long& val) { int64_t v; deserializeInteger(v); val = static_cast(v); return *this; } + Deserializer& operator>>(unsigned long& val) { uint64_t v; deserializeInteger(v); val = static_cast(v); return *this; } +#endif Deserializer& operator>>(float& val) { return deserializePod(val); } Deserializer& operator>>(double& val) { return deserializePod(val); } diff --git a/src/engine/core/src/os/os_mac.cpp b/src/engine/core/src/os/os_mac.cpp index 5c5e70c468..ad753d18fc 100644 --- a/src/engine/core/src/os/os_mac.cpp +++ b/src/engine/core/src/os/os_mac.cpp @@ -51,12 +51,15 @@ Path OSMac::parseProgramPath(const String&) char buffer[2048]; uint32_t bufSize = 2048; _NSGetExecutablePath(buffer, &bufSize); - Path programPath = Path(String(buffer)).parentPath() / "."; + // Return the full executable path; Environment::parseProgramPath derives the + // program directory from this via parentPath(), matching other platforms. + const Path executablePath = Path(String(buffer)); + const Path programDir = executablePath.parentPath() / "."; - std::cout << "Setting CWD to " << programPath << std::endl; - chdir(programPath.string().c_str()); + std::cout << "Setting CWD to " << programDir << std::endl; + chdir(programDir.string().c_str()); - return programPath; + return executablePath; } void OSMac::openURL(const String& url) diff --git a/src/plugins/avf/src/avf_movie_api.h b/src/plugins/avf/src/avf_movie_api.h index ef06a3b75f..180db38354 100644 --- a/src/plugins/avf/src/avf_movie_api.h +++ b/src/plugins/avf/src/avf_movie_api.h @@ -10,7 +10,7 @@ namespace Halley { void deInit() override; bool canPlayVideo() const override { return true; } - std::shared_ptr makePlayer(VideoAPI& video, AudioAPI& audio, std::shared_ptr data) override; + std::shared_ptr makePlayer(const HalleyAPI& halleyAPI, std::shared_ptr data) override; private: SystemAPI& system; diff --git a/src/plugins/avf/src/avf_movie_api.mm b/src/plugins/avf/src/avf_movie_api.mm index 3e09276fe3..3d06944083 100644 --- a/src/plugins/avf/src/avf_movie_api.mm +++ b/src/plugins/avf/src/avf_movie_api.mm @@ -15,7 +15,7 @@ { } -std::shared_ptr AVFMovieAPI::makePlayer(VideoAPI& video, AudioAPI& audio, std::shared_ptr data) +std::shared_ptr AVFMovieAPI::makePlayer(const HalleyAPI& halleyAPI, std::shared_ptr data) { - return std::make_shared(video, audio, data); + return std::make_shared(halleyAPI, data); } diff --git a/src/plugins/avf/src/avf_movie_player.h b/src/plugins/avf/src/avf_movie_player.h index fd6e12f91f..529337b10e 100644 --- a/src/plugins/avf/src/avf_movie_player.h +++ b/src/plugins/avf/src/avf_movie_player.h @@ -12,11 +12,12 @@ namespace Halley { class VideoAPI; + class HalleyAPI; class AVFMoviePlayer : public MoviePlayer { public: - AVFMoviePlayer(VideoAPI& video, AudioAPI& audio, std::shared_ptr data); + AVFMoviePlayer(const HalleyAPI& halleyAPI, std::shared_ptr data); ~AVFMoviePlayer() noexcept; protected: diff --git a/src/plugins/avf/src/avf_movie_player.mm b/src/plugins/avf/src/avf_movie_player.mm index 49b8bb28b5..f07c9e9c4b 100644 --- a/src/plugins/avf/src/avf_movie_player.mm +++ b/src/plugins/avf/src/avf_movie_player.mm @@ -8,8 +8,8 @@ using namespace Halley; -AVFMoviePlayer::AVFMoviePlayer(VideoAPI& video, AudioAPI& audio, std::shared_ptr data) - : MoviePlayer(video, audio) +AVFMoviePlayer::AVFMoviePlayer(const HalleyAPI& halleyAPI, std::shared_ptr data) + : MoviePlayer(halleyAPI) , data(std::move(data)) { init(); diff --git a/src/plugins/opengl/src/shader_opengl.cpp b/src/plugins/opengl/src/shader_opengl.cpp index dcfd98d215..a556d33ab7 100644 --- a/src/plugins/opengl/src/shader_opengl.cpp +++ b/src/plugins/opengl/src/shader_opengl.cpp @@ -47,7 +47,9 @@ ShaderOpenGL::ShaderOpenGL(const ShaderDefinition& definition) id = glCreateProgram(); glCheckError(); #ifdef WITH_OPENGL - glObjectLabel(GL_PROGRAM, id, -1, definition.name.c_str()); + if (glObjectLabel) { // null unless KHR_debug is available (e.g. not on macOS OpenGL 4.1) + glObjectLabel(GL_PROGRAM, id, -1, definition.name.c_str()); + } #endif name = definition.name; @@ -86,7 +88,9 @@ static GLuint loadShader(const Bytes& src, GLenum type, String name) GLuint shader = glCreateShader(type); glCheckError(); #ifdef WITH_OPENGL - glObjectLabel(GL_SHADER, shader, -1, name.c_str()); + if (glObjectLabel) { // null unless KHR_debug is available (e.g. not on macOS OpenGL 4.1) + glObjectLabel(GL_SHADER, shader, -1, name.c_str()); + } #endif // Load source diff --git a/src/plugins/opengl/src/texture_opengl.cpp b/src/plugins/opengl/src/texture_opengl.cpp index a69903c206..37485f7bef 100644 --- a/src/plugins/opengl/src/texture_opengl.cpp +++ b/src/plugins/opengl/src/texture_opengl.cpp @@ -179,7 +179,9 @@ void TextureOpenGL::create(Vector2i size, TextureFormat format, bool useMipMap, glCheckError(); #ifdef WITH_OPENGL - glObjectLabel(GL_TEXTURE, textureId, -1, getAssetId().c_str()); + if (glObjectLabel) { // null unless KHR_debug is available (e.g. not on macOS OpenGL 4.1) + glObjectLabel(GL_TEXTURE, textureId, -1, getAssetId().c_str()); + } #endif texSize = size; diff --git a/src/tests/src/vector_test.cpp b/src/tests/src/vector_test.cpp index 569802ae50..d45f85c99c 100644 --- a/src/tests/src/vector_test.cpp +++ b/src/tests/src/vector_test.cpp @@ -61,7 +61,7 @@ namespace { a.push_back(std::move(a.back())); } - EXPECT_EQ(a.front(), T::value_type()); + EXPECT_EQ(a.front(), typename T::value_type()); EXPECT_EQ(a.back(), v); } @@ -74,8 +74,8 @@ namespace { a.push_back(std::move(a.back())); } - EXPECT_EQ(a.front(), T::value_type()); - EXPECT_NE(a.back(), T::value_type()); + EXPECT_EQ(a.front(), typename T::value_type()); + EXPECT_NE(a.back(), typename T::value_type()); } template @@ -132,7 +132,7 @@ namespace { if constexpr (std::is_same_v) { a.push_back(toString(val)); } else { - a.push_back(T::value_type(val)); + a.push_back(typename T::value_type(val)); } EXPECT_TRUE(a.sbo_active()); } diff --git a/src/tools/tools/src/assets/importers/shader_importer_dxc.cpp b/src/tools/tools/src/assets/importers/shader_importer_dxc.cpp index cecc7053dd..03248a2ed5 100644 --- a/src/tools/tools/src/assets/importers/shader_importer_dxc.cpp +++ b/src/tools/tools/src/assets/importers/shader_importer_dxc.cpp @@ -8,12 +8,14 @@ #include #include using namespace Microsoft::WRL; -#endif +// Uses D3D12 types; only valid on Windows #include "shader_importer_dxc.inl" +#endif using namespace Halley; +#ifdef _MSC_VER static DxcCreateInstanceProc getDxcCreateInstanceFunction(const char* dllName) { // NOTE: This leaks the DLL module, FreeLibrary() is never called. @@ -34,6 +36,7 @@ static DxcCreateInstanceProc getDxcCreateInstanceFunction(const char* dllName) return fn; } +#endif Bytes ShaderImporterDXC::compileDXIL(const String& name, ShaderType type, const Bytes& bytes, const String& language, const MaterialDefinition& material) { #ifdef _MSC_VER From 0c26bb93289a805da0ebfaf8b55bdc3990f7eebb Mon Sep 17 00:00:00 2001 From: Paul Barrass Date: Wed, 17 Jun 2026 01:08:54 +0100 Subject: [PATCH 2/2] Document native arm64 macOS build The bundled deps/osx libraries are x86_64-only, so building on Apple Silicon uses current Homebrew dependencies plus a from-source ShaderConductor. Add a README section covering the deps, the ShaderConductor build flags needed under modern Apple Clang, and the CMake configure invocation. --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f7532ec3f..47c17d2fc8 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Halley is divided in a several sub-projects: ## Platforms The following platforms are supported: * **Windows**: Tested on Windows 10 Professional 64-bit (Might work on as low as XP 32-bit, but XP is no longer a tested target) -* **Mac OS X**: Tested on Mac OS X 10.9.6 +* **macOS**: Tested on Mac OS X 10.9.6 (Intel); also builds and runs natively on Apple Silicon (arm64) — see [macOS (Apple Silicon)](#macos-apple-silicon--arm64) below * **Linux**: Tested on Ubuntu 16.04 ## Installation @@ -94,5 +94,28 @@ The following platforms are supported: * Run `halley-editor tests/entity` (or whichever other project you want to test) * Launch that project +### macOS (Apple Silicon / arm64) +The bundled `deps/osx` libraries are x86_64-only, so a native arm64 build pulls current dependencies from [Homebrew](https://brew.sh) instead: +``` +brew install freetype sdl2 googletest openssl +``` +(`yaml-cpp` is vendored under `src/contrib`, so its Homebrew package is optional.) + +[ShaderConductor](https://github.com/microsoft/ShaderConductor) (required for the tools/editor) has no arm64 prebuilt, so build it from source. Its pinned DXC/LLVM fork needs a couple of flags to compile under recent Apple Clang — configure it with `-DCMAKE_CXX_FLAGS=-Wno-invalid-specialization -DSPIRV_WERROR=OFF`, and drop the `-march=core2 -msse2` / `-Werror` lines that `Source/CMakeLists.txt` forces for non-arm hosts. This yields an arm64 `libShaderConductor.dylib`. + +Configure Halley with Ninja, pointing CMake at the Homebrew prefix and your ShaderConductor build. The explicit `SDL2_*` paths matter, otherwise CMake picks up the Windows-configured SDL2 headers under `deps/include`: +``` +cmake -G Ninja \ + -DBUILD_HALLEY_TOOLS=1 -DBUILD_HALLEY_TESTS=1 \ + -DCMAKE_PREFIX_PATH=/opt/homebrew \ + -DSDL2_INCLUDE_DIR=/opt/homebrew/include/SDL2 \ + -DSDL2_LIBRARIES=/opt/homebrew/lib/libSDL2.dylib \ + -DShaderConductor_INCLUDE_DIR="/Include;/Include/ShaderConductor" \ + -DShaderConductor_LIBRARY=/Build/.../Lib/libShaderConductor.dylib \ + .. +cmake --build . --config RelWithDebInfo +``` +Then launch the editor with `./bin/halley-editor --dont-load-dll`. + ## Documentation The full documentation is available on the [Wiki](https://github.com/amzeratul/halley/wiki).