From 3590426ab8059cec74220340470138ae0b3aa515 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 13 Jun 2026 04:22:09 -0700 Subject: [PATCH] mac: Migrate to KosmicKrisp (#4566) --- .github/workflows/build.yml | 5 +-- .gitmodules | 7 ++-- CMakeLists.txt | 27 ++++---------- README.md | 2 +- REUSE.toml | 1 - documents/building-macos.md | 2 +- externals/CMakeLists.txt | 10 ------ externals/MoltenVK | 1 - externals/mesa-kosmickrisp | 1 + externals/vulkan-headers | 2 +- src/main.cpp | 22 ++++++++++-- .../translate/vector_interpolation.cpp | 3 +- src/shader_recompiler/profile.h | 1 - .../renderer_vulkan/vk_instance.cpp | 35 +------------------ src/video_core/renderer_vulkan/vk_instance.h | 12 ------- .../renderer_vulkan/vk_pipeline_cache.cpp | 15 +++----- .../renderer_vulkan/vk_platform.cpp | 27 ++------------ 17 files changed, 44 insertions(+), 129 deletions(-) delete mode 160000 externals/MoltenVK create mode 160000 externals/mesa-kosmickrisp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f54dc518..34f3005f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -233,8 +233,9 @@ jobs: run: | mkdir upload mv ${{github.workspace}}/build/shadps4 upload - mv ${{github.workspace}}/build/MoltenVK_icd.json upload - mv ${{github.workspace}}/build/libMoltenVK.dylib upload + mv ${{github.workspace}}/build/libvulkan.dylib upload + mv ${{github.workspace}}/build/libvulkan_kosmickrisp.dylib upload + mv ${{github.workspace}}/build/kosmickrisp_mesa_icd.json upload - uses: actions/upload-artifact@v7 with: name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }} diff --git a/.gitmodules b/.gitmodules index 12476e36a..b0b663c6b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -99,10 +99,6 @@ url = https://github.com/shadps4-emu/ext-wepoll.git shallow = true branch = dist -[submodule "externals/MoltenVK"] - path = externals/MoltenVK - url = https://github.com/shadPS4-emu/ext-MoltenVK.git - shallow = true [submodule "externals/json"] path = externals/json url = https://github.com/nlohmann/json.git @@ -151,3 +147,6 @@ [submodule "externals/miniupnp"] path = externals/miniupnp url = https://github.com/miniupnp/miniupnp.git +[submodule "externals/mesa-kosmickrisp"] + path = externals/mesa-kosmickrisp + url = https://github.com/shadexternals/mesa-kosmickrisp diff --git a/CMakeLists.txt b/CMakeLists.txt index 407d25898..89878147b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) if(APPLE) list(APPEND ADDITIONAL_LANGUAGES OBJC) - # Starting with 15.4, Rosetta 2 has support for all the necessary instruction sets. - set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4 CACHE STRING "") + # Minimum OS version for KosmicKrisp + set(CMAKE_OSX_DEPLOYMENT_TARGET 26.0 CACHE STRING "") endif() if (NOT CMAKE_BUILD_TYPE) @@ -246,7 +246,7 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.4.329 CONFIG) +find_package(VulkanHeaders 1.4.330 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) @@ -1216,25 +1216,10 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() if (APPLE) - # Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. + # Bundle Vulkan loader and KosmicKrisp driver with the emulator. set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path") - set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}) - set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/MoltenVK/libMoltenVK.dylib) - set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib) - set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/icd/MoltenVK_icd.json) - set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json) - - add_custom_command( - OUTPUT ${MVK_ICD_DST} - DEPENDS ${MVK_ICD_SRC} ${MVK_DST} - COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST}) - add_custom_command( - OUTPUT ${MVK_DYLIB_DST} - DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST} - COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) - add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST}) - add_dependencies(CopyMoltenVK MoltenVK) - add_dependencies(shadps4 CopyMoltenVK) + add_subdirectory(externals/mesa-kosmickrisp) + AddCopyKosmicKrisp(shadps4 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) if (ARCHITECTURE STREQUAL "x86_64") # Reserve system-managed memory space. diff --git a/README.md b/README.md index 5fec0efd2..3861bba62 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-macos.md). > [!IMPORTANT] -> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs. +> macOS users need at least macOS 26.0 to run shadPS4. Intel Macs are not supported. # Usage examples diff --git a/REUSE.toml b/REUSE.toml index 741784afc..911d2b849 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -21,7 +21,6 @@ path = [ "documents/Screenshots/*", "documents/Screenshots/Linux/*", "documents/Screenshots/Windows/*", - "externals/MoltenVK/MoltenVK_icd.json", "flake.lock", "scripts/ps4_names.txt", "src/images/bronze.png", diff --git a/documents/building-macos.md b/documents/building-macos.md index d96f82952..a36988ce1 100644 --- a/documents/building-macos.md +++ b/documents/building-macos.md @@ -7,7 +7,7 @@ SPDX-License-Identifier: GPL-2.0-or-later ### Install the necessary tools to build shadPS4: -First, make sure you have **Xcode 16.0 or newer** installed. +First, make sure you have **Xcode 26.0 or newer** installed. For installing other tools and library dependencies we will be using [Homebrew](https://brew.sh/). diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bc16b789e..a7660bbc2 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -260,16 +260,6 @@ if (APPLE OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") endif() endif() -# Apple-only dependencies -if (APPLE) - # MoltenVK - if (NOT TARGET MoltenVK) - set(MVK_EXCLUDE_SPIRV_TOOLS ON) - set(MVK_USE_METAL_PRIVATE_API ON) - add_subdirectory(MoltenVK) - endif() -endif() - #windows only if (WIN32) add_subdirectory(ext-wepoll) diff --git a/externals/MoltenVK b/externals/MoltenVK deleted file mode 160000 index a14cbf433..000000000 --- a/externals/MoltenVK +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a14cbf433414aa95da7046694b069f0f3f9263a5 diff --git a/externals/mesa-kosmickrisp b/externals/mesa-kosmickrisp new file mode 160000 index 000000000..801e2de8e --- /dev/null +++ b/externals/mesa-kosmickrisp @@ -0,0 +1 @@ +Subproject commit 801e2de8ef2e3e89fd7ee1da52d67a1294ad6135 diff --git a/externals/vulkan-headers b/externals/vulkan-headers index 33d7f5125..ee3b5caaa 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit 33d7f512583b8de44d1b6384aa1cf482f92e53e9 +Subproject commit ee3b5caaa7e372715873c7b9c390ee1c3ca5db25 diff --git a/src/main.cpp b/src/main.cpp index f4095b679..4dd8026bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,28 +9,44 @@ #include #include -#include -#include +#include "common/arch.h" #include "common/key_manager.h" #include "common/logging/log.h" #include "common/memory_patcher.h" #include "common/path_util.h" #include "core/debugger.h" +#include "core/emulator_settings.h" +#include "core/emulator_state.h" #include "core/file_sys/fs.h" #include "core/ipc/ipc.h" +#include "core/user_settings.h" #include "emulator.h" #include "imgui/big_picture/big_picture.h" #ifdef _WIN32 #include +#elif defined(__APPLE__) +#include #endif -#include int main(int argc, char* argv[]) { #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif +#if defined(__APPLE__) && defined(ARCH_X86_64) + // KosmicKrisp only supports Apple Silicon. Check that we are not running on an Intel Mac. + int sysctl_ret = 0; + size_t sysctl_size = sizeof(sysctl_ret); + sysctlbyname("sysctl.proc_translated", &sysctl_ret, &sysctl_size, nullptr, 0); + if (sysctl_ret != 1) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "shadPS4", + "shadPS4 only supports Apple Silicon Macs.", nullptr); + std::cout << "shadPS4 only supports Apple Silicon Macs." << std::endl; + return -1; + } +#endif + CLI::App app{"shadPS4 Emulator CLI"}; // ---- CLI state ---- diff --git a/src/shader_recompiler/frontend/translate/vector_interpolation.cpp b/src/shader_recompiler/frontend/translate/vector_interpolation.cpp index f3da22845..d5d058593 100644 --- a/src/shader_recompiler/frontend/translate/vector_interpolation.cpp +++ b/src/shader_recompiler/frontend/translate/vector_interpolation.cpp @@ -100,8 +100,7 @@ void Translator::V_INTERP_MOV_F32(const GcnInst& inst) { auto& interp = info.fs_interpolation[attr_index]; ASSERT(attr.is_flat || inst.src[0].code == 2); if (profile.supports_amd_shader_explicit_vertex_parameter || - (profile.supports_fragment_shader_barycentric && - !profile.has_incomplete_fragment_shader_barycentric)) { + profile.supports_fragment_shader_barycentric) { // VSRC 0=P10, 1=P20, 2=P0 interp.primary = Qualifier::PerVertex; SetDst(inst.dst[0], diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 8b96865c1..7ce262f98 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -44,7 +44,6 @@ struct Profile { bool supports_workgroup_explicit_memory_layout{}; bool supports_amd_shader_explicit_vertex_parameter{}; bool supports_fragment_shader_barycentric{}; - bool has_incomplete_fragment_shader_barycentric{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 411a5caef..1be825476 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -201,7 +201,6 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceRobustness2FeaturesEXT, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, - vk::PhysicalDevicePortabilitySubsetFeaturesKHR, vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT, vk::PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR, vk::PhysicalDeviceImage2DViewOf3DFeaturesEXT>(); @@ -335,21 +334,10 @@ bool Instance::CreateDevice() { LOG_INFO(Render_Vulkan, "- sampler2DViewOf3D: {}", image_2d_view_of_3d_features.sampler2DViewOf3D); } + supports_memory_budget = add_extension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME); const bool calibrated_timestamps = TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; -#ifdef __APPLE__ - if (driver_id == vk::DriverId::eMoltenvk) { - portability_subset = add_extension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); - if (portability_subset) { - portability_features = - feature_chain.get(); - } - } -#endif - - supports_memory_budget = add_extension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME); - const auto family_properties = physical_device.getQueueFamilyProperties(); if (family_properties.empty()) { LOG_CRITICAL(Render_Vulkan, "Physical device reported no queues."); @@ -514,27 +502,6 @@ bool Instance::CreateDevice() { .image2DViewOf3D = image_2d_view_of_3d_features.image2DViewOf3D, .sampler2DViewOf3D = image_2d_view_of_3d_features.sampler2DViewOf3D, }, -#ifdef __APPLE__ - vk::PhysicalDevicePortabilitySubsetFeaturesKHR{ - .constantAlphaColorBlendFactors = portability_features.constantAlphaColorBlendFactors, - .events = portability_features.events, - .imageViewFormatReinterpretation = portability_features.imageViewFormatReinterpretation, - .imageViewFormatSwizzle = portability_features.imageViewFormatSwizzle, - .imageView2DOn3DImage = portability_features.imageView2DOn3DImage, - .multisampleArrayImage = portability_features.multisampleArrayImage, - .mutableComparisonSamplers = portability_features.mutableComparisonSamplers, - .pointPolygons = portability_features.pointPolygons, - .samplerMipLodBias = portability_features.samplerMipLodBias, - .separateStencilMaskRef = portability_features.separateStencilMaskRef, - .shaderSampleRateInterpolationFunctions = - portability_features.shaderSampleRateInterpolationFunctions, - .tessellationIsolines = portability_features.tessellationIsolines, - .tessellationPointMode = portability_features.tessellationPointMode, - .triangleFans = portability_features.triangleFans, - .vertexAttributeAccessBeyondStride = - portability_features.vertexAttributeAccessBeyondStride, - }, -#endif }; if (!custom_border_color) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index f170e3f34..1ac45eaa5 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -270,16 +270,6 @@ public: return features.tessellationShader; } - /// Returns true when tessellation isolines are supported by the device - bool IsTessellationIsolinesSupported() const { - return !portability_subset || portability_features.tessellationIsolines; - } - - /// Returns true when tessellation point mode is supported by the device - bool IsTessellationPointModeSupported() const { - return !portability_subset || portability_features.tessellationPointMode; - } - /// Returns the vendor ID of the physical device u32 GetVendorID() const { return properties.vendorID; @@ -483,7 +473,6 @@ private: vk::PhysicalDeviceFeatures features; vk::PhysicalDeviceVulkan12Features vk12_features; vk::PhysicalDeviceVulkan13Features vk13_features; - vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features; vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3_features; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2_features; vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT shader_atomic_float2_features; @@ -523,7 +512,6 @@ private: bool shader_atomic_float{}; bool shader_atomic_float2{}; bool workgroup_memory_explicit_layout{}; - bool portability_subset{}; bool maintenance_8{}; bool attachment_feedback_loop{}; bool image_2d_view_of_3d{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 90365c307..a78dd3f1d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -298,15 +298,12 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_amd_shader_explicit_vertex_parameter = instance_.IsAmdShaderExplicitVertexParameterSupported(), .supports_fragment_shader_barycentric = instance_.IsFragmentShaderBarycentricSupported(), - .has_incomplete_fragment_shader_barycentric = - instance_.IsFragmentShaderBarycentricSupported() && - instance.GetDriverID() == vk::DriverId::eMoltenvk, .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || - instance.GetDriverID() == vk::DriverId::eMoltenvk, + instance.GetDriverID() == vk::DriverId::eMesaKosmickrisp, .needs_buffer_offsets = instance.StorageMinAlignment() > 4, - .needs_unorm_fixup = instance.GetDriverID() == vk::DriverId::eMoltenvk, + .needs_unorm_fixup = instance.GetDriverID() == vk::DriverId::eMesaKosmickrisp, .needs_clip_distance_emulation = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .supports_shader_stencil_export = instance_.IsShaderStencilExportSupported(), }; @@ -532,9 +529,7 @@ bool PipelineCache::RefreshGraphicsStages() { } break; case AmdGpu::ShaderStageEnable::VgtStages::LsHs: - if (!instance.IsTessellationSupported() || - (regs.tess_config.type == AmdGpu::TessellationType::Isoline && - !instance.IsTessellationIsolinesSupported())) { + if (!instance.IsTessellationSupported()) { return false; } if (!bind_stage(Stage::Hull, LogicalStage::TessellationControl)) { @@ -548,9 +543,7 @@ bool PipelineCache::RefreshGraphicsStages() { } break; case AmdGpu::ShaderStageEnable::VgtStages::LsHsEsGs: - if (!instance.IsTessellationSupported() || - (regs.tess_config.type == AmdGpu::TessellationType::Isoline && - !instance.IsTessellationIsolinesSupported())) { + if (!instance.IsTessellationSupported()) { return false; } if (!instance.IsGeometryStageSupported()) { diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 83bb3c6aa..6b8f6b142 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -192,10 +192,6 @@ std::vector GetInstanceExtensions(Frontend::WindowSystemType window break; } -#ifdef __APPLE__ - extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); -#endif - if (window_type != Frontend::WindowSystemType::Headless) { extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); } @@ -262,14 +258,14 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e bool enable_crash_diagnostic) { LOG_INFO(Render_Vulkan, "Creating vulkan instance"); -#if defined(__APPLE__) && !defined(ENABLE_QT_GUI) - // Initialize the environment with the path to the MoltenVK ICD, so that the loader will +#if defined(__APPLE__) + // Initialize the environment with the path to the included ICD, so that the loader will // find it. static const auto icd_path = [] { char path[PATH_MAX]; u32 length = PATH_MAX; _NSGetExecutablePath(path, &length); - return std::filesystem::path(path).parent_path() / "MoltenVK_icd.json"; + return std::filesystem::path(path).parent_path(); }(); setenv("VK_DRIVER_FILES", icd_path.c_str(), true); #endif @@ -315,9 +311,6 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e Common::FS::GetUserPathString(Common::FS::PathType::LogDir); const char* log_path = crash_diagnostic_path.c_str(); vk::Bool32 enable_force_barriers = vk::True; -#ifdef __APPLE__ - const vk::Bool32 mvk_debug_mode = enable_crash_diagnostic ? vk::True : vk::False; -#endif const std::array layer_setings = { vk::LayerSettingEXT{ @@ -404,24 +397,10 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e .valueCount = 1, .pValues = &enable_force_barriers, }, -#ifdef __APPLE__ - // MoltenVK debug mode turns on additional device loss error details, so - // use the crash diagnostic setting as an indicator of whether to turn it on. - vk::LayerSettingEXT{ - .pLayerName = "MoltenVK", - .pSettingName = "MVK_CONFIG_DEBUG", - .type = vk::LayerSettingTypeEXT::eBool32, - .valueCount = 1, - .pValues = &mvk_debug_mode, - }, -#endif }; vk::StructureChain instance_ci_chain = { vk::InstanceCreateInfo{ -#ifdef __APPLE__ - .flags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR, -#endif .pApplicationInfo = &application_info, .enabledLayerCount = static_cast(layers.size()), .ppEnabledLayerNames = layers.data(),