From 7d6953e4be92f598772d3c22132ad2193ca025ea Mon Sep 17 00:00:00 2001 From: Umais Khan Date: Thu, 25 Dec 2025 13:43:11 -0800 Subject: [PATCH 1/3] Attempting to add MacOS support to the client --- client/conanfile.py | 2 +- rehtiLib/graphics/CMakeLists.txt | 9 ++++----- scripts/build-client.sh | 17 +++++++++++++++-- scripts/generate_assets.sh | 28 ++++++++++++++++++++-------- scripts/run-client.sh | 23 ++++++++++++++++++----- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/client/conanfile.py b/client/conanfile.py index 1b18fea6..dbd042c6 100644 --- a/client/conanfile.py +++ b/client/conanfile.py @@ -16,7 +16,7 @@ def requirements(self): self.requires("spirv-tools/1.3.239.0", override=True) self.requires("glfw/3.3.8") self.requires("vulkan-memory-allocator/3.0.1") - self.requires("stb/cci.20220909") + self.requires("stb/cci.20230920", override=True) # updated to match assimp self.requires("glm/cci.20230113") self.requires("vulkan-headers/1.3.239.0", override=True) self.requires("imgui/cci.20230105+1.89.2.docking") diff --git a/rehtiLib/graphics/CMakeLists.txt b/rehtiLib/graphics/CMakeLists.txt index 3936d81d..aad4e75c 100644 --- a/rehtiLib/graphics/CMakeLists.txt +++ b/rehtiLib/graphics/CMakeLists.txt @@ -1,14 +1,13 @@ add_library(rehtiGraphics "") set(DEPENDENCY_DIR "${CMAKE_CURRENT_LIST_DIR}/../../third_party") -set(VALIDATION_LAYER_DIR "${vulkan-validationlayers_INCLUDE_DIR}/../${CMAKE_INSTALL_BINDIR}") -set($ENV{VK_LAYER_PATH} VALIDATION_LAYER_DIR) -set($ENV{VK_LOADER_LAYERS_ENABLE} "*validation") +# We *do not* set VK_LAYER_PATH here anymore. +# We'll set it in the shell/run script instead, where it actually matters at runtime. find_package(glfw3 3.3.8 REQUIRED) find_package(Vulkan 1.3.239.0 REQUIRED) find_package(SPIRV-Tools 1.3.239.0 REQUIRED) -find_package(vulkan-memory-allocator 3.0.1 REQUIRED) +find_package(vulkan-memory-allocator CONFIG REQUIRED) find_package(vulkan-validationlayers 1.3.239.0 REQUIRED) find_package(stb REQUIRED) find_package(glm REQUIRED) @@ -77,7 +76,7 @@ target_link_libraries(rehtiGraphics glfw spirv-tools::spirv-tools ${GLSLANG_LIBS} - vulkan-memory-allocator::vulkan-memory-allocator + vulkan-memory-allocator::vulkan-memory-allocator vulkan-validationlayers::vulkan-validationlayers stb::stb glm::glm diff --git a/scripts/build-client.sh b/scripts/build-client.sh index e0ddebfd..50ef6690 100755 --- a/scripts/build-client.sh +++ b/scripts/build-client.sh @@ -1,6 +1,19 @@ #!/bin/bash +set -e + +# 1) Generate assets ./scripts/generate_assets.sh + +# 2) Move into client directory cd ./client + +# 3) Have Conan resolve dependencies and generate toolchain into ./build conan install . --output-folder=build --build=missing -s build_type=Debug -s compiler.cppstd=20 -cmake -S . -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -B build -DCMAKE_BUILD_TYPE=Debug -cmake --build ./build \ No newline at end of file + +# 4) Configure CMake using the Conan toolchain +cmake -S . -B build -G "Unix Makefiles" \ + -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE=Debug + +# 5) Build the client +cmake --build build --config Debug diff --git a/scripts/generate_assets.sh b/scripts/generate_assets.sh index d92d1600..3be0a16c 100755 --- a/scripts/generate_assets.sh +++ b/scripts/generate_assets.sh @@ -1,18 +1,30 @@ #!/bin/bash -# Add log here -echo ">>>>>> RUNNING ASSET GENERATION SCRIPT <<<<<<\n" +echo ">>>>>> RUNNING ASSET GENERATION SCRIPT <<<<<<" -if [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then +UNAME_S="$(uname -s)" + +case "$UNAME_S" in + Linux*) # Linux EXECUTABLE_PATH="./rehtiLib/assets/loader/build/asset_loader" -elif [[ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" || "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]]; then + ;; + + Darwin*) + # macOS (including Sequoia / Darwin 25) + EXECUTABLE_PATH="./rehtiLib/assets/loader/build/asset_loader" + ;; + + MINGW32_NT*|MINGW64_NT*) # Windows (MINGW32_NT or MINGW64_NT) EXECUTABLE_PATH="./rehtiLib/assets/loader/build/Debug/asset_loader" -else - echo "Unsupported operating system" + ;; + + *) + echo "Unsupported operating system: $UNAME_S" exit 1 -fi + ;; +esac cmake -S ./rehtiLib/assets/loader -B ./rehtiLib/assets/loader/build -DCMAKE_BUILD_TYPE=Debug cmake --build ./rehtiLib/assets/loader/build @@ -25,4 +37,4 @@ else exit 1 fi -echo ">>>>>> ASSET GENERATION SCRIPT FINISHED <<<<<<\n" \ No newline at end of file +echo ">>>>>> ASSET GENERATION SCRIPT FINISHED <<<<<<" diff --git a/scripts/run-client.sh b/scripts/run-client.sh index ad6d1323..979d8646 100755 --- a/scripts/run-client.sh +++ b/scripts/run-client.sh @@ -1,15 +1,28 @@ #!/bin/bash -if [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then +UNAME_S="$(uname -s)" + +case "$UNAME_S" in + Linux*) # Linux EXECUTABLE_PATH="./client/build/Client" -elif [[ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" || "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]]; then + ;; + + Darwin*) + # macOS (including Sequoia / Darwin 25) + EXECUTABLE_PATH="./client/build/Client" + ;; + + MINGW32_NT*|MINGW64_NT*) # Windows (MINGW32_NT or MINGW64_NT) EXECUTABLE_PATH="./client/build/Debug/Client" -else - echo "Unsupported operating system" + ;; + + *) + echo "Unsupported operating system: $UNAME_S" exit 1 -fi + ;; +esac ./scripts/build-client.sh From 4cee5f0846881fa62a34a0c63c6bce7b6b571471 Mon Sep 17 00:00:00 2001 From: Umais Khan Date: Thu, 25 Dec 2025 13:50:13 -0800 Subject: [PATCH 2/3] Added third_party to the .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 91926a8f..df911623 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,5 @@ generated docs/html #imgui.ini file -imgui.ini \ No newline at end of file +imgui.ini +third_party/ From 407a0782d1742f8fa4792c5913c7cf12735f7cad Mon Sep 17 00:00:00 2001 From: Juvariya Lat <66323781+juvariyalat@users.noreply.github.com> Date: Thu, 25 Dec 2025 17:19:00 -0600 Subject: [PATCH 3/3] Trying to get client to run on MacOS with the help of Claude --- client/CMakeLists.txt | 16 ++- client/Info.plist.in | 30 ++++++ client/conanfile.py | 2 +- client/src/Client.cpp | 64 ++++++++---- rehtiLib/graphics/CMakeLists.txt | 20 +++- rehtiLib/graphics/src/RehtiGraphics.cpp | 128 ++++++++++++++++++++++-- rehtiLib/graphics/src/RehtiGraphics.hpp | 3 +- scripts/run-client.sh | 24 ++++- 8 files changed, 254 insertions(+), 33 deletions(-) create mode 100644 client/Info.plist.in diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 10336a57..7ca05872 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -15,7 +15,21 @@ set(SOURCES list(TRANSFORM SOURCES PREPEND ${CMAKE_CURRENT_LIST_DIR}/src/) -add_executable(${PROJECT_NAME} ${SOURCES}) +# Create macOS app bundle on Apple platforms +if(APPLE) + add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SOURCES}) + + # Set bundle properties + set_target_properties(${PROJECT_NAME} PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "Rehti MMORPG Client" + MACOSX_BUNDLE_GUI_IDENTIFIER "com.rehti.mmorpg.client" + MACOSX_BUNDLE_BUNDLE_VERSION "1.0" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in" + ) +else() + add_executable(${PROJECT_NAME} ${SOURCES}) +endif() add_subdirectory(../rehtiLib/graphics ${CMAKE_CURRENT_LIST_DIR}/build/graphics) add_subdirectory(../rehtiLib/network ${CMAKE_CURRENT_LIST_DIR}/build/network) diff --git a/client/Info.plist.in b/client/Info.plist.in new file mode 100644 index 00000000..2c180817 --- /dev/null +++ b/client/Info.plist.in @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIdentifier + com.rehti.mmorpg.client + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.15 + NSHighResolutionCapable + + NSSupportsAutomaticGraphicsSwitching + + NSPrincipalClass + NSApplication + + diff --git a/client/conanfile.py b/client/conanfile.py index dbd042c6..d5b31766 100644 --- a/client/conanfile.py +++ b/client/conanfile.py @@ -16,7 +16,7 @@ def requirements(self): self.requires("spirv-tools/1.3.239.0", override=True) self.requires("glfw/3.3.8") self.requires("vulkan-memory-allocator/3.0.1") - self.requires("stb/cci.20230920", override=True) # updated to match assimp + self.requires("stb/cci.20240531", override=True) # updated to latest version self.requires("glm/cci.20230113") self.requires("vulkan-headers/1.3.239.0", override=True) self.requires("imgui/cci.20230105+1.89.2.docking") diff --git a/client/src/Client.cpp b/client/src/Client.cpp index a3b43b42..c4e997cc 100644 --- a/client/src/Client.cpp +++ b/client/src/Client.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "Client.hpp" #include "RehtiReader.hpp" @@ -19,30 +20,34 @@ Client::Client(std::string ip, std::string port) Connection::owner::client, ioContextM, std::move(boost::asio::ip::tcp::socket(ioContextM)), messagesM)), audioLibM{} { - graphicsThreadM = std::thread([this]() - { startGraphics(); }); - + // On macOS, GLFW/graphics must run on the main thread + // So we start IO and other work on background threads instead ioThreadM = std::thread([this]() { ioContextM.run(); }); }; void Client::start() { - // Wait for graphics library to be ready - if (!graphLibReadyFlagM) - { - std::unique_lock ul(graphLibMutexM); - graphLibReadyM.wait(ul); - } + // Start network and message processing on background threads connectionThreadM = std::thread([this]() { boost::asio::co_spawn(ioContextM, connect(), boost::asio::detached); }); std::thread messageThread([this]() { processMessages(); }); - // Cleanup after graphics thread has exited - graphicsThreadM.join(); + + // Run graphics on the main thread (required for macOS) + // This will block until the window is closed + startGraphics(); + + // Cleanup after graphics has exited connectionM->disconnect(); ioContextM.stop(); + + // Wait for background threads to finish + if (connectionThreadM.joinable()) + connectionThreadM.join(); + if (messageThread.joinable()) + messageThread.join(); } boost::asio::awaitable Client::login() @@ -437,13 +442,19 @@ void Client::handleMouseClick(const Hit& hit) void Client::startGraphics() { + try + { + std::cout << "[DEBUG] Starting graphics initialization..." << std::endl; - // Load assets to memory - assetCacheM.loadAssets(); - // init graphics library - pGraphLibM = new RehtiGraphics(); + // Load assets to memory + assetCacheM.loadAssets(); - // Create map bounding box + std::cout << "[DEBUG] Creating RehtiGraphics instance..." << std::endl; + // init graphics library + pGraphLibM = new RehtiGraphics(); + std::cout << "[DEBUG] RehtiGraphics instance created successfully!" << std::endl; + + // Create map bounding box std::vector> heightMatrix = fetchHeightMatrix(); std::vector> areaMatrix = fetchAreaMatrix(); @@ -488,9 +499,22 @@ void Client::startGraphics() pGraphLibM->getGui()->addEquipmentItemClickCallback([this](const int itemInstanceId) { boost::asio::co_spawn(ioContextM, unequipItem(itemInstanceId), boost::asio::detached); }); - std::cout << "Graphics library ready" << std::endl; + std::cout << "Graphics library ready" << std::endl; - graphLibReadyFlagM = true; - graphLibReadyM.notify_one(); - pGraphLibM->startMainLoop(); + graphLibReadyFlagM = true; + graphLibReadyM.notify_one(); + pGraphLibM->startMainLoop(); + } + catch (const std::exception& e) + { + std::cerr << "[ERROR] Exception in startGraphics: " << e.what() << std::endl; + std::cerr << "[ERROR] Graphics initialization failed!" << std::endl; + throw; + } + catch (...) + { + std::cerr << "[ERROR] Unknown exception in startGraphics!" << std::endl; + std::cerr << "[ERROR] Graphics initialization failed!" << std::endl; + throw; + } } diff --git a/rehtiLib/graphics/CMakeLists.txt b/rehtiLib/graphics/CMakeLists.txt index aad4e75c..824d16e2 100644 --- a/rehtiLib/graphics/CMakeLists.txt +++ b/rehtiLib/graphics/CMakeLists.txt @@ -7,9 +7,23 @@ set(DEPENDENCY_DIR "${CMAKE_CURRENT_LIST_DIR}/../../third_party") find_package(glfw3 3.3.8 REQUIRED) find_package(Vulkan 1.3.239.0 REQUIRED) find_package(SPIRV-Tools 1.3.239.0 REQUIRED) -find_package(vulkan-memory-allocator CONFIG REQUIRED) +find_package(VulkanMemoryAllocator CONFIG REQUIRED) find_package(vulkan-validationlayers 1.3.239.0 REQUIRED) -find_package(stb REQUIRED) +# stb doesn't provide CMake config via Conan, manually locate and create interface library +if(NOT TARGET stb::stb) + # Try to find stb_image.h in the Conan cache + execute_process( + COMMAND bash -c "find ~/.conan2/p/stb*/p/include -name stb_image.h 2>/dev/null | head -1 | xargs dirname" + OUTPUT_VARIABLE STB_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT STB_INCLUDE_DIR) + message(FATAL_ERROR "Could not find stb include directory. Make sure stb is installed via Conan.") + endif() + message(STATUS "Found stb include directory: ${STB_INCLUDE_DIR}") + add_library(stb::stb INTERFACE IMPORTED) + target_include_directories(stb::stb INTERFACE ${STB_INCLUDE_DIR}) +endif() find_package(glm REQUIRED) find_package(imgui REQUIRED) @@ -76,7 +90,7 @@ target_link_libraries(rehtiGraphics glfw spirv-tools::spirv-tools ${GLSLANG_LIBS} - vulkan-memory-allocator::vulkan-memory-allocator + GPUOpen::VulkanMemoryAllocator vulkan-validationlayers::vulkan-validationlayers stb::stb glm::glm diff --git a/rehtiLib/graphics/src/RehtiGraphics.cpp b/rehtiLib/graphics/src/RehtiGraphics.cpp index 7da9f0c0..34cc06ae 100644 --- a/rehtiLib/graphics/src/RehtiGraphics.cpp +++ b/rehtiLib/graphics/src/RehtiGraphics.cpp @@ -14,9 +14,24 @@ RehtiGraphics::RehtiGraphics(uint32_t width, uint32_t height, glm::vec3 cameraLo : widthM(width), heightM(height), anisotropyM(1.f), cameraM(Camera(cameraLocation, static_cast(width), static_cast(height))), sunM({glm::vec3(1.f, -1.f, 1.f), glm::vec3(1.f), 1.f}) { - initWindow(); - cameraM.registerCameraControls(pWindowM); - initVulkan(); + try + { + std::cout << "[DEBUG] RehtiGraphics constructor starting..." << std::endl; + initWindow(); + cameraM.registerCameraControls(pWindowM); + initVulkan(); + std::cout << "[DEBUG] ✅ RehtiGraphics constructor complete!" << std::endl; + } + catch (const std::exception& e) + { + std::cerr << "[ERROR] Exception in RehtiGraphics constructor: " << e.what() << std::endl; + throw; + } + catch (...) + { + std::cerr << "[ERROR] Unknown exception in RehtiGraphics constructor!" << std::endl; + throw; + } } RehtiGraphics::~RehtiGraphics() @@ -401,16 +416,35 @@ void RehtiGraphics::setEngineFlags(EngineFlags flags) void RehtiGraphics::initWindow() { + std::cout << "[DEBUG] Starting GLFW initialization..." << std::endl; + // Initialize glfw - glfwInit(); + if (!glfwInit()) + { + std::cerr << "[ERROR] Failed to initialize GLFW!" << std::endl; + throw std::runtime_error("GLFW initialization failed"); + } + std::cout << "[DEBUG] GLFW initialized successfully" << std::endl; + // Some arguments glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + std::cout << "[DEBUG] Creating GLFW window..." << std::endl; pWindowM = glfwCreateWindow(widthM, heightM, "REHTI MMORPG", nullptr, nullptr); + if (!pWindowM) + { + std::cerr << "[ERROR] Failed to create GLFW window!" << std::endl; + glfwTerminate(); + throw std::runtime_error("GLFW window creation failed"); + } + std::cout << "[DEBUG] GLFW window created successfully" << std::endl; + glfwSetWindowUserPointer(pWindowM, this); glfwSetFramebufferSizeCallback(pWindowM, RehtiGraphics::frameBufferResizeCallback); + + std::cout << "[DEBUG] Window initialization complete" << std::endl; } void RehtiGraphics::frameBufferResizeCallback(GLFWwindow* window, int width, int height) @@ -421,23 +455,77 @@ void RehtiGraphics::frameBufferResizeCallback(GLFWwindow* window, int width, int void RehtiGraphics::initVulkan() { + std::cout << "[DEBUG] Starting Vulkan initialization..." << std::endl; + + std::cout << "[DEBUG] Creating Vulkan instance..." << std::endl; createInstance(); + std::cout << "[DEBUG] Vulkan instance created" << std::endl; + + std::cout << "[DEBUG] Setting up debug messenger..." << std::endl; setupDebugMessenger(); // Setup debugging + std::cout << "[DEBUG] Debug messenger setup complete" << std::endl; + + std::cout << "[DEBUG] Creating surface..." << std::endl; createSurface(); // Create the surface to draw into. Mostly handled by glfw. + std::cout << "[DEBUG] Surface created" << std::endl; + + std::cout << "[DEBUG] Picking physical device..." << std::endl; pickPhysicalDevice(); // Choose the physical device (gpu) + std::cout << "[DEBUG] Physical device picked" << std::endl; + + std::cout << "[DEBUG] Creating logical device..." << std::endl; createLogicalDevice(); // Create the interactable logical device + std::cout << "[DEBUG] Logical device created" << std::endl; + + std::cout << "[DEBUG] Creating texture sampler..." << std::endl; createTextureSampler(); // Create a sampler for textures + std::cout << "[DEBUG] Texture sampler created" << std::endl; + + std::cout << "[DEBUG] Creating object manager..." << std::endl; createObjectManager(); // Initializes the object management + std::cout << "[DEBUG] Object manager created" << std::endl; + + std::cout << "[DEBUG] Creating swap chain..." << std::endl; createSwapChain(); // Creates the swapchain + std::cout << "[DEBUG] Swap chain created" << std::endl; + + std::cout << "[DEBUG] Creating depth resources..." << std::endl; createDepthResources(); + std::cout << "[DEBUG] Depth resources created" << std::endl; + + std::cout << "[DEBUG] Creating image views..." << std::endl; createImageViews(); // Creates the image view (how to access the image) + std::cout << "[DEBUG] Image views created" << std::endl; + + std::cout << "[DEBUG] Creating render pass..." << std::endl; createRenderPass(); + std::cout << "[DEBUG] Render pass created" << std::endl; + + std::cout << "[DEBUG] Creating graphics pipeline..." << std::endl; createGraphicsPipeline(); // Creates a rendering pipeline + std::cout << "[DEBUG] Graphics pipeline created" << std::endl; + + std::cout << "[DEBUG] Creating framebuffers..." << std::endl; createFramebuffers(); // Creates the framebuffers + std::cout << "[DEBUG] Framebuffers created" << std::endl; + + std::cout << "[DEBUG] Creating command pool..." << std::endl; createCommandPool(); + std::cout << "[DEBUG] Command pool created" << std::endl; + + std::cout << "[DEBUG] Creating command buffers..." << std::endl; createCommandBuffers(); + std::cout << "[DEBUG] Command buffers created" << std::endl; + + std::cout << "[DEBUG] Creating synchronization objects..." << std::endl; createSynchronization(); + std::cout << "[DEBUG] Synchronization objects created" << std::endl; + + std::cout << "[DEBUG] Creating GUI..." << std::endl; createGui(); + std::cout << "[DEBUG] GUI created" << std::endl; + + std::cout << "[DEBUG] ✅ Vulkan initialization complete!" << std::endl; } void RehtiGraphics::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) @@ -527,8 +615,17 @@ void RehtiGraphics::createLogicalDevice() devCreateInfo.pEnabledFeatures = &deviceFeatures; - devCreateInfo.enabledExtensionCount = static_cast(kDeviceExtensionsM.size()); - devCreateInfo.ppEnabledExtensionNames = kDeviceExtensionsM.data(); + // Prepare device extensions + std::vector deviceExtensions(kDeviceExtensionsM.begin(), kDeviceExtensionsM.end()); + +#ifdef __APPLE__ + // Add portability subset extension for MoltenVK + deviceExtensions.push_back("VK_KHR_portability_subset"); + std::cout << "[DEBUG] Added VK_KHR_portability_subset extension for macOS" << std::endl; +#endif + + devCreateInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + devCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); // TODO: this is not correct, as there can be layers other than validation layers if (validationLayersEnabledM) @@ -1178,12 +1275,24 @@ void RehtiGraphics::createInstance() info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); info.pEngineName = "RehtiEngine"; info.engineVersion = VK_MAKE_VERSION(1, 0, 0); +#ifdef __APPLE__ + // MoltenVK supports up to Vulkan 1.2 + info.apiVersion = VK_API_VERSION_1_2; + std::cout << "[DEBUG] Using Vulkan API 1.2 for macOS/MoltenVK" << std::endl; +#else info.apiVersion = VK_API_VERSION_1_3; // Vulkan 1.3 +#endif // Create info: VkInstanceCreateInfo instanceInfo{}; instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceInfo.pApplicationInfo = &info; + +#ifdef __APPLE__ + // Required for MoltenVK portability + instanceInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + // Next up, extensions auto extensions = this->getRequiredExtensions(); @@ -1529,6 +1638,13 @@ std::vector RehtiGraphics::getRequiredExtensions() extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } +#ifdef __APPLE__ + // Required for MoltenVK portability + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + extensions.push_back("VK_KHR_get_physical_device_properties2"); + std::cout << "[DEBUG] Added macOS portability extensions" << std::endl; +#endif + return extensions; } diff --git a/rehtiLib/graphics/src/RehtiGraphics.hpp b/rehtiLib/graphics/src/RehtiGraphics.hpp index fd90379d..4c5cbb02 100644 --- a/rehtiLib/graphics/src/RehtiGraphics.hpp +++ b/rehtiLib/graphics/src/RehtiGraphics.hpp @@ -616,7 +616,8 @@ class RehtiGraphics static void frameBufferResizeCallback(GLFWwindow* window, int width, int height); -#ifdef NDEBUG +#if defined(NDEBUG) || defined(__APPLE__) + // Disable validation layers in Release builds or on macOS (where they're not commonly installed) const bool enableValidationLayers = false; #else const bool enableValidationLayers = true; diff --git a/scripts/run-client.sh b/scripts/run-client.sh index 979d8646..f02522ce 100755 --- a/scripts/run-client.sh +++ b/scripts/run-client.sh @@ -10,7 +10,29 @@ case "$UNAME_S" in Darwin*) # macOS (including Sequoia / Darwin 25) - EXECUTABLE_PATH="./client/build/Client" + + # Configure MoltenVK for Vulkan support on macOS + if [ -f "/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json" ]; then + export VK_ICD_FILENAMES=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json + export DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH + echo "MoltenVK configured (Apple Silicon)" + elif [ -f "/usr/local/etc/vulkan/icd.d/MoltenVK_icd.json" ]; then + export VK_ICD_FILENAMES=/usr/local/etc/vulkan/icd.d/MoltenVK_icd.json + export DYLD_LIBRARY_PATH=/usr/local/lib:$DYLD_LIBRARY_PATH + echo "MoltenVK configured (Intel)" + else + echo "WARNING: MoltenVK not found. Install with: brew install molten-vk" + fi + + # Enable MoltenVK debug output (optional, comment out if too verbose) + export MVK_CONFIG_LOG_LEVEL=3 + + # Check if app bundle exists, otherwise fall back to plain executable + if [ -d "./client/build/Client.app" ]; then + EXECUTABLE_PATH="./client/build/Client.app/Contents/MacOS/Client" + else + EXECUTABLE_PATH="./client/build/Client" + fi ;; MINGW32_NT*|MINGW64_NT*)