diff --git a/.github/workflows/apple.yml b/.github/workflows/apple.yml index e98f152e9..798c52671 100644 --- a/.github/workflows/apple.yml +++ b/.github/workflows/apple.yml @@ -140,6 +140,8 @@ jobs: lipo -create ./macosx-x86_64/supertuxkart.app/Contents/MacOS/supertuxkart ./macosx-arm64/supertuxkart.app/Contents/MacOS/supertuxkart -output ./macosx-arm64/supertuxkart.app/Contents/MacOS/supertuxkart chmod 755 ./macosx-arm64/supertuxkart.app/Contents/MacOS/supertuxkart dylibbundler -od -b -x ./macosx-arm64/supertuxkart.app/Contents/MacOS/supertuxkart -d ./macosx-arm64/supertuxkart.app/Contents/libs/ -p @executable_path/../libs/ -s dependencies-macosx/lib + # We use SDL_Vulkan_LoadLibrary for 10.9 compatibility, so otool -L supertuxkart has no libMoltenVK.dylib + cp ./dependencies-macosx/lib/libMoltenVK.dylib ./macosx-arm64/supertuxkart.app/Contents/libs/ cd ./macosx-arm64/supertuxkart.app/Contents/Resources/data wget https://github.com/supertuxkart/stk-assets-mobile/releases/download/git/stk-assets-full.zip unzip stk-assets-full.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index db4c05a90..83667929c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ if(NINTENDO_SWITCH) endif() if(WIN32) -option(USE_DIRECTX "Build DirectX 9 driver (requires DirectX SDK)" OFF) + option(USE_DIRECTX "Build DirectX 9 driver (requires DirectX SDK)" OFF) endif() option(SERVER_ONLY "Create a server only (i.e. no graphics or sound)" OFF) @@ -38,6 +38,11 @@ CMAKE_DEPENDENT_OPTION(USE_IPV6 "Allow create or connect to game server with IPv option(USE_SYSTEM_WIIUSE "Use system WiiUse instead of the built-in version, when available." OFF) option(USE_SQLITE3 "Use sqlite to manage server stats and ban list." ON) +if(APPLE) + CMAKE_DEPENDENT_OPTION(DLOPEN_MOLTENVK "Use dlopen to load MoltenVK for Apple." ON + "NOT IOS;NOT SERVER_ONLY" OFF) +endif() + CMAKE_DEPENDENT_OPTION(USE_CRYPTO_OPENSSL "Use OpenSSL instead of MbedTLS for cryptography in STK." ON "NOT USE_SWITCH;NOT WIN32" OFF) CMAKE_DEPENDENT_OPTION(BUILD_RECORDER "Build opengl recorder" ON @@ -49,6 +54,10 @@ CMAKE_DEPENDENT_OPTION(USE_WIIUSE "Support for wiimote input devices" ON CMAKE_DEPENDENT_OPTION(USE_DNS_C "Build bundled dns resolver" OFF "NOT CYGWIN;NOT USE_SWITCH" ON) CMAKE_DEPENDENT_OPTION(USE_MOJOAL "Use bundled MojoAL instead of system OpenAL" OFF "NOT APPLE" ON) +if (DLOPEN_MOLTENVK) + ADD_DEFINITIONS(-DDLOPEN_MOLTENVK) +endif() + if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH) include(FindPkgConfig) endif() @@ -674,6 +683,10 @@ else() endif() if(NOT SERVER_ONLY) + if (APPLE AND NOT DLOPEN_MOLTENVK) + find_library(MOLTENVK_LIBRARY NAMES MoltenVK libMoltenVK REQUIRED) + target_link_libraries(supertuxkart "-framework Metal -weak_framework IOSurface -framework QuartzCore ${MOLTENVK_LIBRARY}") + endif() if (IOS) target_link_libraries(supertuxkart "-weak_framework CoreHaptics -framework QuartzCore -framework CoreGraphics -framework AVFoundation -framework AudioToolbox -framework Metal -framework GameController -framework OpenGLES -framework UIKit -framework CoreAudio -framework Foundation -framework GLKit") # tvOS doesn't have CoreMotion framwork diff --git a/cmake/Toolchain-ios-xcode.cmake b/cmake/Toolchain-ios-xcode.cmake index 385227258..adc49f5f2 100644 --- a/cmake/Toolchain-ios-xcode.cmake +++ b/cmake/Toolchain-ios-xcode.cmake @@ -7,7 +7,7 @@ # You can also use -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=xxxxxxxxxx to specify team # Increase every upload to App store -SET(IOS_BUILD_VERSION 23) +SET(IOS_BUILD_VERSION 24) # Get SDK path execute_process(COMMAND xcodebuild -version -sdk iphoneos Path @@ -57,6 +57,8 @@ set(SDL2_LIBRARY ${PROJECT_SOURCE_DIR}/dependencies\${EFFECTIVE_PLATFORM_NAME}/l set(SDL2_INCLUDEDIR ${PROJECT_SOURCE_DIR}/dependencies-iphoneos/include/SDL2 CACHE STRING "") set(LIBSAMPLERATE_LIBRARY ${PROJECT_SOURCE_DIR}/dependencies\${EFFECTIVE_PLATFORM_NAME}/lib/libsamplerate.a CACHE STRING "") set(LIBSAMPLERATE_INCLUDEDIR ${PROJECT_SOURCE_DIR}/dependencies-iphoneos/include CACHE STRING "") +set(MOLTENVK_LIBRARY ${PROJECT_SOURCE_DIR}/dependencies\${EFFECTIVE_PLATFORM_NAME}/lib/libMoltenVK.a CACHE STRING "") +set(VULKAN_INCLUDEDIR ${PROJECT_SOURCE_DIR}/dependencies-iphoneos/include CACHE STRING "") # For universal iOS and simulator set(LIBRESOLV_LIBRARY -lresolv CACHE STRING "") diff --git a/lib/graphics_engine/CMakeLists.txt b/lib/graphics_engine/CMakeLists.txt index dd34ed733..c1659babb 100644 --- a/lib/graphics_engine/CMakeLists.txt +++ b/lib/graphics_engine/CMakeLists.txt @@ -7,15 +7,30 @@ else() include_directories("${SDL2_INCLUDEDIR}") endif() +if(APPLE AND NOT DLOPEN_MOLTENVK) + find_path(VULKAN_INCLUDEDIR NAMES vulkan/vulkan.h PATHS) + if (NOT VULKAN_INCLUDEDIR) + message(FATAL_ERROR "Vulkan not found.") + else() + include_directories("${VULKAN_INCLUDEDIR}") + endif() +endif() + if(UNIX OR MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") endif() -add_library(graphics_engine STATIC + +set(GE_SOURCES src/gl.c - src/vulkan.c src/ge_main.cpp src/ge_texture.cpp src/ge_dx9_texture.cpp src/ge_vulkan_driver.cpp src/ge_gl_texture.cpp ) + +if(NOT APPLE OR DLOPEN_MOLTENVK) + set(GE_SOURCES ${GE_SOURCES} src/vulkan.c) +endif() + +add_library(graphics_engine STATIC ${GE_SOURCES}) diff --git a/lib/graphics_engine/include/ge_vulkan_driver.hpp b/lib/graphics_engine/include/ge_vulkan_driver.hpp index 7c7a53855..e1e4ec3dd 100644 --- a/lib/graphics_engine/include/ge_vulkan_driver.hpp +++ b/lib/graphics_engine/include/ge_vulkan_driver.hpp @@ -5,7 +5,7 @@ #ifdef _IRR_COMPILE_WITH_VULKAN_ -#include "glad/vulkan.h" +#include "vulkan_wrapper.h" #include "SDL_video.h" #include "../source/Irrlicht/CNullDriver.h" diff --git a/lib/graphics_engine/include/vulkan_wrapper.h b/lib/graphics_engine/include/vulkan_wrapper.h new file mode 100644 index 000000000..b00d629a8 --- /dev/null +++ b/lib/graphics_engine/include/vulkan_wrapper.h @@ -0,0 +1,10 @@ +#ifndef HEADER_VULKAN_WRAPPER_HPP +#define HEADER_VULKAN_WRAPPER_HPP + +#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) +#include +#else +#include +#endif + +#endif diff --git a/lib/graphics_engine/src/ge_vulkan_driver.cpp b/lib/graphics_engine/src/ge_vulkan_driver.cpp index 1d6853765..743c5f79c 100644 --- a/lib/graphics_engine/src/ge_vulkan_driver.cpp +++ b/lib/graphics_engine/src/ge_vulkan_driver.cpp @@ -8,6 +8,7 @@ #include "../source/Irrlicht/os.h" +#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) struct GE_VK_UserPointer { VkInstance instance; @@ -430,6 +431,7 @@ extern "C" PFN_vkVoidFunction loader(void* user_ptr, const char* name) return NULL; return get_instance_proc_addr(instance, name); } // loader +#endif namespace GE { @@ -446,6 +448,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, createInstance(window); +#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) GE_VK_UserPointer user_ptr = {}; user_ptr.instance = m_vk.instance; if (gladLoadVulkanUserPtr(NULL, @@ -454,12 +457,15 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, throw std::runtime_error("gladLoadVulkanUserPtr failed " "with non-NULL instance"); } +#endif + if (SDL_Vulkan_CreateSurface(window, m_vk.instance, &m_vk.surface) == SDL_FALSE) throw std::runtime_error("SDL_Vulkan_CreateSurface failed"); m_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); findPhysicalDevice(); createDevice(); +#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) user_ptr.device = m_vk.device; if (gladLoadVulkanUserPtr(m_physical_device, (GLADuserptrloadfunc)loader, &user_ptr) == 0) @@ -467,6 +473,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, throw std::runtime_error("gladLoadVulkanUserPtr failed with " "non-NULL instance and non-NULL m_physical_device"); } +#endif vkGetPhysicalDeviceProperties(m_physical_device, &m_properties); os::Printer::log("Vulkan version", getVulkanVersionString().c_str()); @@ -485,10 +492,13 @@ GEVulkanDriver::~GEVulkanDriver() // ---------------------------------------------------------------------------- void GEVulkanDriver::createInstance(SDL_Window* window) { +#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) if (gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc)loader, NULL) == 0) { throw std::runtime_error("gladLoadVulkanUserPtr failed 1st time"); } +#endif + unsigned int count = 0; if (!SDL_Vulkan_GetInstanceExtensions(window, &count, NULL)) throw std::runtime_error("SDL_Vulkan_GetInstanceExtensions failed with NULL extensions"); diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index bbcb96a94..e7f84cfe8 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -531,6 +531,15 @@ include/vector2d.h include/vector3d.h ) +if(DLOPEN_MOLTENVK) + set(IRRLICHT_SOURCES + ${IRRLICHT_SOURCES} + source/Irrlicht/MoltenVK.mm + source/Irrlicht/MoltenVK.h) + set_source_files_properties(source/Irrlicht/MoltenVK.mm PROPERTIES COMPILE_FLAGS "-x objective-c++ -O3 -fno-rtti") + set_source_files_properties(source/Irrlicht/MoltenVK.mm PROPERTIES LANGUAGE C) +endif() + if(IOS) set(IRRLICHT_SOURCES ${IRRLICHT_SOURCES} diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp index 1adf2bcb2..06d81284c 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp @@ -21,6 +21,7 @@ #include "guiengine/engine.hpp" #include "glad/gl.h" +#include "MoltenVK.h" extern bool GLContextDebugBit; @@ -71,6 +72,10 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param) setDebugName("CIrrDeviceSDL"); #endif +#ifdef DLOPEN_MOLTENVK + m_moltenvk = NULL; +#endif + Operator = 0; // Initialize SDL... Timer for sleep, video for the obvious, and // noparachute prevents SDL from catching fatal errors. @@ -194,6 +199,9 @@ CIrrDeviceSDL::~CIrrDeviceSDL() VideoDriver->drop(); VideoDriver = NULL; } +#ifdef DLOPEN_MOLTENVK + delete m_moltenvk; +#endif if (Context) SDL_GL_DeleteContext(Context); if (Window) @@ -359,7 +367,17 @@ bool CIrrDeviceSDL::createWindow() CreationParams.DriverType == video::EDT_OGLES2) flags |= SDL_WINDOW_OPENGL; else if (CreationParams.DriverType == video::EDT_VULKAN) + { +#ifdef DLOPEN_MOLTENVK + m_moltenvk = new MoltenVK(); + if (!m_moltenvk->loaded()) + { + os::Printer::log("Current MacOSX version doesn't support Vulkan or MoltenVK failed to load", ELL_WARNING); + return false; + } +#endif flags |= SDL_WINDOW_VULKAN; + } #ifdef MOBILE_STK flags |= SDL_WINDOW_BORDERLESS | SDL_WINDOW_MAXIMIZED; diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.h b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.h index 9ca74c396..08ec78345 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.h +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.h @@ -23,6 +23,7 @@ namespace irr { +class MoltenVK; class CIrrDeviceSDL : public CIrrDeviceStub, video::IImagePresenter { @@ -323,6 +324,9 @@ namespace irr std::map ScanCodeMap; SDL_SysWMinfo Info; void tryCreateOpenGLContext(u32 flags); +#ifdef DLOPEN_MOLTENVK + MoltenVK* m_moltenvk; +#endif }; } // end namespace irr diff --git a/lib/irrlicht/source/Irrlicht/MoltenVK.h b/lib/irrlicht/source/Irrlicht/MoltenVK.h new file mode 100644 index 000000000..b2fd20ecc --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/MoltenVK.h @@ -0,0 +1,23 @@ +#ifndef HEADER_MAC_VULKAN_HPP +#define HEADER_MAC_VULKAN_HPP + +#ifdef DLOPEN_MOLTENVK +namespace irr +{ +class MoltenVK +{ +private: + bool m_loaded; +public: + // ------------------------------------------------------------------------ + MoltenVK(); + // ------------------------------------------------------------------------ + ~MoltenVK(); + // ------------------------------------------------------------------------ + bool loaded() const { return m_loaded; } +}; +} + +#endif + +#endif diff --git a/lib/irrlicht/source/Irrlicht/MoltenVK.mm b/lib/irrlicht/source/Irrlicht/MoltenVK.mm new file mode 100644 index 000000000..dbe4ab9d4 --- /dev/null +++ b/lib/irrlicht/source/Irrlicht/MoltenVK.mm @@ -0,0 +1,43 @@ +#include "MoltenVK.h" +#include "SDL_vulkan.h" +#import + +#ifdef DLOPEN_MOLTENVK + +namespace irr +{ +// ---------------------------------------------------------------------------- +MoltenVK::MoltenVK() +{ + m_loaded = false; + // MacOSX 10.11 or later supports Metal (MoltenVK) + if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_10_Max) + return; + const char* paths[3] = + { + // STK release binary path after dylibbundler + "@executable_path/../libs/libMoltenVK.dylib", + // bin/supertuxkart.app/Contents/MacOS/supertuxkart + "@executable_path/../../../../../dependencies-macosx/lib/libMoltenVK.dylib", + "NULL" + }; + for (int i = 0; i < 3; i++) + { + if (SDL_Vulkan_LoadLibrary(paths[i]) == 0) + { + m_loaded = true; + break; + } + } +} // MoltenVK + +// ---------------------------------------------------------------------------- +MoltenVK::~MoltenVK() +{ + if (m_loaded) + SDL_Vulkan_UnloadLibrary(); +} // ~MoltenVK + +#endif + +}