diff --git a/Directory.Packages.props b/Directory.Packages.props index f19f283f1..be19c901f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -36,8 +36,10 @@ + + @@ -47,7 +49,6 @@ - diff --git a/distribution/macos/construct_universal_dylib.py b/distribution/macos/construct_universal_dylib.py index 18b84399f..69462d1b2 100644 --- a/distribution/macos/construct_universal_dylib.py +++ b/distribution/macos/construct_universal_dylib.py @@ -48,7 +48,7 @@ def get_new_name( return Path(os.path.join(output_directory, input_component)) def get_archs(dylib_path: Path) -> list[str]: - res = subprocess.check_output([LIPO, "-info", str(dylib_path)]).decode("utf-8") + res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode("utf-8") if res.startswith("Non-fat file"): return [res.split(":")[-1].strip()] else: diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index 7a1461eb6..d317f7b71 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -18,14 +18,18 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources" cp "$PUBLISH_DIRECTORY/Ryujinx" "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx" chmod u+x "$APP_BUNDLE_DIRECTORY/Contents/MacOS/Ryujinx" +# List all dylibs in PUBLISH_DIRECTORY +ls -la "$PUBLISH_DIRECTORY"/*.dylib + # Then all libraries -cp "$PUBLISH_DIRECTORY"/*.dylib "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" +find "$PUBLISH_DIRECTORY" -name "*.dylib" -exec cp {} "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" \; # Then resources cp Info.plist "$APP_BUNDLE_DIRECTORY/Contents" cp Ryujinx.icns "$APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns" cp updater.sh "$APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh" cp Assets.car "$APP_BUNDLE_DIRECTORY/Contents/Resources/Assets.car" +cp -r vulkan "$APP_BUNDLE_DIRECTORY/Contents/Resources" cp -r "$PUBLISH_DIRECTORY/THIRDPARTY.md" "$APP_BUNDLE_DIRECTORY/Contents/Resources" echo -n "APPL????" > "$APP_BUNDLE_DIRECTORY/Contents/PkgInfo" diff --git a/distribution/macos/create_macos_build_ava.sh b/distribution/macos/create_macos_build_ava.sh index a45cee552..534d2f332 100755 --- a/distribution/macos/create_macos_build_ava.sh +++ b/distribution/macos/create_macos_build_ava.sh @@ -47,7 +47,7 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx rm -rf "$TEMP_DIRECTORY" mkdir -p "$TEMP_DIRECTORY" -DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS) +DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" -p:UseSharedCompilation=false --self-contained true $EXTRA_ARGS) dotnet restore dotnet build -c "$CONFIGURATION" src/Ryujinx @@ -76,6 +76,9 @@ rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" # Make it libraries universal python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib" +# Copy arm64-only libraries to UNIVERSAL_APP_BUNDLE +cp "$ARM64_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" + if ! [ -x "$(command -v lipo)" ]; then if ! [ -x "$(command -v llvm-lipo-17)" ]; diff --git a/distribution/macos/vulkan/icd.d/MoltenVK_icd.json b/distribution/macos/vulkan/icd.d/MoltenVK_icd.json new file mode 100644 index 000000000..104f0698a --- /dev/null +++ b/distribution/macos/vulkan/icd.d/MoltenVK_icd.json @@ -0,0 +1,8 @@ +{ + "file_format_version" : "1.0.0", + "ICD": { + "library_path": "../../../Frameworks/libMoltenVK.dylib", + "api_version" : "1.4.0", + "is_portability_driver" : true + } +} diff --git a/distribution/macos/vulkan/icd.d/libkosmickrisp_icd.json b/distribution/macos/vulkan/icd.d/libkosmickrisp_icd.json new file mode 100644 index 000000000..bb002d8d8 --- /dev/null +++ b/distribution/macos/vulkan/icd.d/libkosmickrisp_icd.json @@ -0,0 +1,7 @@ +{ + "ICD": { + "api_version": "1.3.0", + "library_path": "../../../Frameworks/libvulkan_kosmickrisp.dylib" + }, + "file_format_version": "1.0.1" +} \ No newline at end of file diff --git a/nuget.config b/nuget.config index 9b600f3d6..c22fe2d5b 100644 --- a/nuget.config +++ b/nuget.config @@ -2,6 +2,7 @@ + @@ -10,6 +11,9 @@ + + + diff --git a/nuget/Ryujinx.Graphics.Vulkan.KosmicKrisp.26.2.1-ryu.1.nupkg b/nuget/Ryujinx.Graphics.Vulkan.KosmicKrisp.26.2.1-ryu.1.nupkg new file mode 100644 index 000000000..5173c3e7a Binary files /dev/null and b/nuget/Ryujinx.Graphics.Vulkan.KosmicKrisp.26.2.1-ryu.1.nupkg differ diff --git a/src/Ryujinx.Common/Configuration/TranslationLayer.cs b/src/Ryujinx.Common/Configuration/TranslationLayer.cs new file mode 100644 index 000000000..f349bfd89 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/TranslationLayer.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum TranslationLayer + { + MoltenVK, + KosmicKrisp, + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs deleted file mode 100644 index 271999375..000000000 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Vulkan.MoltenVK -{ - enum MVKConfigLogLevel - { - None = 0, - Error = 1, - Warning = 2, - Info = 3, - Debug = 4, - } - - enum MVKConfigTraceVulkanCalls - { - None = 0, - Enter = 1, - EnterExit = 2, - Duration = 3, - } - - enum MVKConfigAutoGPUCaptureScope - { - None = 0, - Device = 1, - Frame = 2, - } - - [Flags] - enum MVKConfigAdvertiseExtensions - { - All = 0x00000001, - MoltenVK = 0x00000002, - WSI = 0x00000004, - Portability = 0x00000008, - } - - enum MVKVkSemaphoreSupportStyle - { - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE = 0, - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1, - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS = 2, - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_CALLBACK = 3, - MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF, - } - - readonly struct Bool32 - { - uint Value { get; } - - public Bool32(uint value) - { - Value = value; - } - - public Bool32(bool value) - { - Value = value ? 1u : 0u; - } - - public static implicit operator bool(Bool32 val) => val.Value == 1; - public static implicit operator Bool32(bool val) => new(val); - } - - [StructLayout(LayoutKind.Sequential)] - struct MVKConfiguration - { - public Bool32 DebugMode; - public Bool32 ShaderConversionFlipVertexY; - public Bool32 SynchronousQueueSubmits; - public Bool32 PrefillMetalCommandBuffers; - public uint MaxActiveMetalCommandBuffersPerQueue; - public Bool32 SupportLargeQueryPools; - public Bool32 PresentWithCommandBuffer; - public Bool32 SwapchainMagFilterUseNearest; - public ulong MetalCompileTimeout; - public Bool32 PerformanceTracking; - public uint PerformanceLoggingFrameCount; - public Bool32 DisplayWatermark; - public Bool32 SpecializedQueueFamilies; - public Bool32 SwitchSystemGPU; - public Bool32 FullImageViewSwizzle; - public uint DefaultGPUCaptureScopeQueueFamilyIndex; - public uint DefaultGPUCaptureScopeQueueIndex; - public Bool32 FastMathEnabled; - public MVKConfigLogLevel LogLevel; - public MVKConfigTraceVulkanCalls TraceVulkanCalls; - public Bool32 ForceLowPowerGPU; - public Bool32 SemaphoreUseMTLFence; - public MVKVkSemaphoreSupportStyle SemaphoreSupportStyle; - public MVKConfigAutoGPUCaptureScope AutoGPUCaptureScope; - public nint AutoGPUCaptureOutputFilepath; - public Bool32 Texture1DAs2D; - public Bool32 PreallocateDescriptors; - public Bool32 UseCommandPooling; - public Bool32 UseMTLHeap; - public Bool32 LogActivityPerformanceInline; - public uint ApiVersionToAdvertise; - public MVKConfigAdvertiseExtensions AdvertiseExtensions; - public Bool32 ResumeLostDevice; - public Bool32 UseMetalArgumentBuffers; - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs deleted file mode 100644 index c3efcc8da..000000000 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Silk.NET.Core.Loader; -using Silk.NET.Vulkan; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.Graphics.Vulkan.MoltenVK -{ - [SupportedOSPlatform("macos")] - public static partial class MVKInitialization - { - private const string VulkanLib = "libvulkan.dylib"; - - [LibraryImport("libMoltenVK.dylib")] - private static partial Result vkGetMoltenVKConfigurationMVK(nint unusedInstance, out MVKConfiguration config, in nint configSize); - - [LibraryImport("libMoltenVK.dylib")] - private static partial Result vkSetMoltenVKConfigurationMVK(nint unusedInstance, in MVKConfiguration config, in nint configSize); - - public static void Initialize() - { - IntPtr configSize = (nint)Marshal.SizeOf(); - - vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize); - - config.UseMetalArgumentBuffers = true; - - config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; - config.SynchronousQueueSubmits = false; - - config.ResumeLostDevice = true; - - vkSetMoltenVKConfigurationMVK(nint.Zero, config, configSize); - } - - private static string[] Resolver(string path) - { - if (path.EndsWith(VulkanLib)) - { - path = path[..^VulkanLib.Length] + "libMoltenVK.dylib"; - return [path]; - } - return []; - } - - public static void InitializeResolver() - { - ((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver); - } - } -} diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 9f564b59f..6451b9a96 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -44,6 +44,7 @@ + diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs index 54460b895..59f5351c2 100644 --- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -99,6 +99,7 @@ namespace Ryujinx.Graphics.Vulkan DriverId.MesaNvk => "NVK", DriverId.ImaginationOpenSourceMesa => "Imagination (Open)", DriverId.MesaHoneykrisp => "Honeykrisp", + DriverId.MesaKosmickrisp => "KosmicKrisp", _ => id.ToString(), }; } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanDriver.cs b/src/Ryujinx.Graphics.Vulkan/VulkanDriver.cs new file mode 100644 index 000000000..a13e65af2 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/VulkanDriver.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Vulkan +{ + [SupportedOSPlatform("macos")] + public static class VulkanDriver + { + private static string GetIcdPath(string fileName) + { + string contentsDir = Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd('/'))!; + string icdPath = Path.Combine(contentsDir, "Resources", "vulkan", "icd.d", fileName); + + return !File.Exists(icdPath) ? throw new FileNotFoundException($"{fileName} not found", icdPath) : icdPath; + } + + public static void MoltenVK() + { + Environment.SetEnvironmentVariable("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1"); + Environment.SetEnvironmentVariable("MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE", "0"); + Environment.SetEnvironmentVariable("MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", "0"); + Environment.SetEnvironmentVariable("MVK_CONFIG_RESUME_LOST_DEVICE", "1"); + + Environment.SetEnvironmentVariable("VK_DRIVER_FILES", GetIcdPath("MoltenVK_icd.json")); + + Console.WriteLine($"[MoltenVK] VK_DRIVER_FILES just set to: {Environment.GetEnvironmentVariable("VK_DRIVER_FILES")}"); + } + + public static void KosmicKrisp() + { + Environment.SetEnvironmentVariable("VK_DRIVER_FILES", GetIcdPath("libkosmickrisp_icd.json")); + + Console.WriteLine($"[KosmicKrisp] VK_DRIVER_FILES just set to: {Environment.GetEnvironmentVariable("VK_DRIVER_FILES")}"); + } + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index d208c11ca..35b2bc737 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -5,7 +5,6 @@ using Silk.NET.Core; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.KHR; -using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -14,10 +13,11 @@ namespace Ryujinx.Graphics.Vulkan { public unsafe static class VulkanInitialization { + private static Version32 Version14 => new(1, 4, 0); + private const uint InvalidIndex = uint.MaxValue; private static readonly uint _minimalVulkanVersion = Vk.Version11.Value; - private static readonly uint _minimalInstanceVulkanVersion = Vk.Version12.Value; - private static readonly uint _maximumVulkanVersion = Vk.Version12.Value; + private static readonly uint _maximumVulkanVersion = Version14.Value; private const string AppName = "Ryujinx.Graphics.Vulkan"; private const int QueuesCount = 2; @@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Vulkan internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions) { - List enabledLayers = new(); + List enabledLayers = []; IReadOnlySet instanceExtensions = VulkanInstance.GetInstanceExtensions(api); IReadOnlySet instanceLayers = VulkanInstance.GetInstanceLayers(api); @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); } - IntPtr appName = Marshal.StringToHGlobalAnsi(AppName); + nint appName = Marshal.StringToHGlobalAnsi(AppName); ApplicationInfo applicationInfo = new() { @@ -168,7 +168,7 @@ namespace Ryujinx.Graphics.Vulkan internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api) { - IntPtr appName = Marshal.StringToHGlobalAnsi(AppName); + nint appName = Marshal.StringToHGlobalAnsi(AppName); ApplicationInfo applicationInfo = new() { @@ -197,13 +197,6 @@ namespace Ryujinx.Graphics.Vulkan using VulkanInstance instance = rawInstance; - // We currently assume that the instance is compatible with Vulkan 1.2 - // TODO: Remove this once we relax our initialization codepaths. - if (instance.InstanceVersion < _minimalInstanceVulkanVersion) - { - return []; - } - instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError(); List deviceInfos = []; @@ -386,7 +379,7 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout; } - PhysicalDevicePageableDeviceLocalMemoryFeaturesEXT supportedPageableDeviceLocalMemoryFeatures = new() + PhysicalDevicePageableDeviceLocalMemoryFeaturesEXT supportedFeaturesPageableDeviceLocalMemory = new() { SType = StructureType.PhysicalDevicePageableDeviceLocalMemoryFeaturesExt, PNext = features2.PNext, @@ -394,10 +387,10 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent(ExtPageableDeviceLocalMemory.ExtensionName)) { - features2.PNext = &supportedPageableDeviceLocalMemoryFeatures; + features2.PNext = &supportedFeaturesPageableDeviceLocalMemory; } - PhysicalDeviceMemoryPriorityFeaturesEXT supportedDeviceMemoryPriorityFeatures = new() + PhysicalDeviceMemoryPriorityFeaturesEXT supportedFeaturesDeviceMemoryPriority = new() { SType = StructureType.PhysicalDeviceMemoryPriorityFeaturesExt, PNext = features2.PNext, @@ -405,7 +398,7 @@ namespace Ryujinx.Graphics.Vulkan if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_memory_priority")) { - features2.PNext = &supportedDeviceMemoryPriorityFeatures; + features2.PNext = &supportedFeaturesDeviceMemoryPriority; } PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() @@ -416,6 +409,22 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedPhysicalDeviceVulkan12Features; + PhysicalDeviceVulkan13Features supportedPhysicalDeviceVulkan13Features = new() + { + SType = StructureType.PhysicalDeviceVulkan13Features, + PNext = features2.PNext, + }; + + features2.PNext = &supportedPhysicalDeviceVulkan13Features; + + PhysicalDeviceVulkan14Features supportedPhysicalDeviceVulkan14Features = new() + { + SType = StructureType.PhysicalDeviceVulkan14Features, + PNext = features2.PNext, + }; + + features2.PNext = &supportedPhysicalDeviceVulkan14Features; + api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2); PhysicalDeviceFeatures supportedFeatures = features2.Features; @@ -519,6 +528,24 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresVk12; + PhysicalDeviceVulkan13Features featuresVk13 = new() + { + SType = StructureType.PhysicalDeviceVulkan13Features, + PNext = pExtendedFeatures, + /* Vulkan 1.3 features here */ + }; + + pExtendedFeatures = &featuresVk13; + + PhysicalDeviceVulkan13Features featuresVk14 = new() + { + SType = StructureType.PhysicalDeviceVulkan14Features, + PNext = pExtendedFeatures, + /* Vulkan 1.4 features here */ + }; + + pExtendedFeatures = &featuresVk14; + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8")) { PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8 = new() @@ -598,7 +625,7 @@ namespace Ryujinx.Graphics.Vulkan } if (physicalDevice.IsDeviceExtensionPresent(ExtPageableDeviceLocalMemory.ExtensionName) && - supportedPageableDeviceLocalMemoryFeatures.PageableDeviceLocalMemory) + supportedFeaturesPageableDeviceLocalMemory.PageableDeviceLocalMemory) { PhysicalDevicePageableDeviceLocalMemoryFeaturesEXT featuresPageableDeviceLocalMemory = new() { @@ -611,16 +638,16 @@ namespace Ryujinx.Graphics.Vulkan } if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_memory_priority") && - supportedDeviceMemoryPriorityFeatures.MemoryPriority) + supportedFeaturesDeviceMemoryPriority.MemoryPriority) { - PhysicalDeviceMemoryPriorityFeaturesEXT featuresDeviceMemoryPriority = new() + PhysicalDeviceMemoryPriorityFeaturesEXT featuresMemoryPriority = new() { SType = StructureType.PhysicalDeviceMemoryPriorityFeaturesExt, PNext = pExtendedFeatures, MemoryPriority = true, }; - pExtendedFeatures = &featuresDeviceMemoryPriority; + pExtendedFeatures = &featuresMemoryPriority; } string[] enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs index 69b75925e..d226e6282 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs @@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Vulkan { private readonly Vk _api; public readonly Instance Instance; - public readonly Version32 InstanceVersion; private bool _disposed; @@ -21,22 +20,6 @@ namespace Ryujinx.Graphics.Vulkan { _api = api; Instance = instance; - - if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == nint.Zero) - { - InstanceVersion = Vk.Version10; - } - else - { - uint rawInstanceVersion = 0; - - if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success) - { - rawInstanceVersion = Vk.Version11.Value; - } - - InstanceVersion = (Version32)rawInstanceVersion; - } } public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance) diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 369534e5e..bab0371e5 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Graphics.Vulkan.Queries; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.EXT; @@ -97,6 +96,7 @@ namespace Ryujinx.Graphics.Vulkan internal bool IsIntelArc { get; private set; } internal bool IsQualcommProprietary { get; private set; } internal bool IsMoltenVk { get; private set; } + internal bool IsKosmicKrisp { get; private set; } internal bool SupportsMTL31 { get; private set; } internal bool IsTBDR { get; private set; } internal bool IsSharedMemory { get; private set; } @@ -111,7 +111,7 @@ namespace Ryujinx.Graphics.Vulkan public event EventHandler ScreenCaptured; - public VulkanRenderer(Vk api, Func surfaceFunc, Func requiredExtensionsFunc, string preferredGpuId) + public VulkanRenderer(Vk api, Func surfaceFunc, Func requiredExtensionsFunc, string preferredGpuId, TranslationLayer translationLayer) { _getSurface = surfaceFunc; _getRequiredExtensions = requiredExtensionsFunc; @@ -121,12 +121,20 @@ namespace Ryujinx.Graphics.Vulkan Textures = []; Samplers = []; + // MoltenVK is required for any device running < macOS 26, even Intel and AMD vendors. + // KosmicKrisp, a Vulkan conformant implementation running on top of Metal 4, requires macOS 26. if (OperatingSystem.IsMacOS()) { - MVKInitialization.Initialize(); - - // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. - IsMoltenVk = true; + if (translationLayer == TranslationLayer.MoltenVK) + { + VulkanDriver.MoltenVK(); + IsMoltenVk = true; + } + if (translationLayer == TranslationLayer.KosmicKrisp) + { + VulkanDriver.KosmicKrisp(); + IsKosmicKrisp = true; + } } SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14); @@ -842,14 +850,14 @@ namespace Ryujinx.Graphics.Vulkan /// /// Gets the available Vulkan devices using the default Vulkan API - /// object returned by + /// object returned by /// /// public static DeviceInfo[] GetPhysicalDevices() { try { - return VulkanInitialization.GetSuitablePhysicalDevices(Vk.GetApi()); + return VulkanInitialization.GetSuitablePhysicalDevices(VulkanSpbApi.GetApiFromSpb()); } catch (Exception ex) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanSpbApi.cs b/src/Ryujinx.Graphics.Vulkan/VulkanSpbApi.cs new file mode 100644 index 000000000..2ce1a881c --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/VulkanSpbApi.cs @@ -0,0 +1,34 @@ +using Silk.NET.Core.Contexts; +using Silk.NET.Vulkan; +using SPB.Graphics.Vulkan; + +namespace Ryujinx.Graphics.Vulkan +{ + public static class VulkanSpbApi + { + public static Vk GetApiFromSpb() + { + LamdaNativeContext baseCtx = new(VulkanHelper.GetProcAddress); + MultiNativeContext ctx = new(baseCtx, null); + Vk ret = new(ctx); + + ctx.Contexts[1] = new LamdaNativeContext(x => + { + if (x.EndsWith("ProcAddr")) + { + return default; + } + + nint ptr = ret.GetDeviceProcAddr(ret.CurrentDevice.GetValueOrDefault(), x); + if (ptr != default) + { + return ptr; + } + + return ret.GetInstanceProcAddr(ret.CurrentInstance.GetValueOrDefault(), x); + }); + + return ret; + } + } +} diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index 995523d1d..0e893fb35 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -439,6 +439,11 @@ namespace Ryujinx.UI.Common.Configuration /// public GraphicsBackend GraphicsBackend { get; set; } + /// + /// Vulkan translation layer + /// + public TranslationLayer TranslationLayer { get; set; } + /// /// Preferred GPU /// diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index e070b944f..9cefdb53d 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -587,6 +587,11 @@ namespace Ryujinx.UI.Common.Configuration /// public ReactiveObject GraphicsBackend { get; private set; } + /// + /// Vulkan translation layer + /// + public ReactiveObject TranslationLayer { get; private set; } + /// /// Applies anti-aliasing to the renderer. /// @@ -639,6 +644,8 @@ namespace Ryujinx.UI.Common.Configuration EnableTextureRecompression.Event += static (_, e) => LogValueChange(e, nameof(EnableTextureRecompression)); GraphicsBackend = new ReactiveObject(); GraphicsBackend.Event += static (_, e) => LogValueChange(e, nameof(GraphicsBackend)); + TranslationLayer = new ReactiveObject(); + TranslationLayer.Event += static (_, e) => LogValueChange(e, nameof(TranslationLayer)); PreferredGpu = new ReactiveObject(); PreferredGpu.Event += static (_, e) => LogValueChange(e, nameof(PreferredGpu)); EnableMacroHLE = new ReactiveObject(); @@ -935,6 +942,7 @@ namespace Ryujinx.UI.Common.Configuration ControllerConfig = [], InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, + TranslationLayer = Graphics.TranslationLayer, PreferredGpu = Graphics.PreferredGpu, MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, MultiplayerMode = Multiplayer.Mode, @@ -958,6 +966,7 @@ namespace Ryujinx.UI.Common.Configuration Graphics.MaxAnisotropy.Value = -1.0f; Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; Graphics.GraphicsBackend.Value = DefaultGraphicsBackend(); + Graphics.TranslationLayer.Value = TranslationLayer.MoltenVK; Graphics.PreferredGpu.Value = ""; Graphics.ShadersDumpPath.Value = ""; Graphics.TexturesDumpPath.Value = ""; @@ -1806,6 +1815,15 @@ namespace Ryujinx.UI.Common.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 62) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 62."); + + configurationFileFormat.TranslationLayer = TranslationLayer.MoltenVK; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1818,6 +1836,7 @@ namespace Ryujinx.UI.Common.Configuration Graphics.EnableTextureRealTimeEdit.Value = configurationFileFormat.GraphicsEnableTextureRealTimeEdit; Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; + Graphics.TranslationLayer.Value = configurationFileFormat.TranslationLayer; Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; diff --git a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs index 77ca77c09..d8233209e 100644 --- a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs @@ -12,6 +12,7 @@ namespace Ryujinx.UI.Common.Helper public static bool? OverrideDockedMode { get; private set; } public static bool? OverrideHardwareAcceleration { get; private set; } public static string OverrideGraphicsBackend { get; private set; } + public static string OverrideTranslationLayer { get; private set; } public static string OverrideBackendThreading { get; private set; } public static string OverrideHideCursor { get; private set; } public static string BaseDirPathArg { get; private set; } @@ -92,6 +93,16 @@ namespace Ryujinx.UI.Common.Helper OverrideGraphicsBackend = args[++i]; break; + case "--translation-layer": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + OverrideTranslationLayer = args[++i]; + break; case "--backend-threading": if (i + 1 >= args.Length) { diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index e40f8319d..1fedb0f45 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -43,7 +43,6 @@ using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; -using Silk.NET.Vulkan; using SkiaSharp; using SPB.Graphics.Exceptions; using SPB.Graphics.Vulkan; @@ -939,10 +938,11 @@ namespace Ryujinx.Ava if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { renderer = new VulkanRenderer( - Vk.GetApi(), + VulkanSpbApi.GetApiFromSpb(), ((EmbeddedWindowVulkan)RendererHost.EmbeddedWindow).CreateSurface, VulkanHelper.GetRequiredInstanceExtensions, - ConfigurationState.Instance.Graphics.PreferredGpu.Value); + ConfigurationState.Instance.Graphics.PreferredGpu.Value, + ConfigurationState.Instance.Graphics.TranslationLayer.Value); } else { @@ -1183,7 +1183,12 @@ namespace Ryujinx.Ava { _viewModel.BackendText = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch { - GraphicsBackend.Vulkan => "Vulkan", + GraphicsBackend.Vulkan when !OperatingSystem.IsMacOS() => "Vulkan", + GraphicsBackend.Vulkan when Environment.GetEnvironmentVariable("VK_DRIVER_FILES")!.Contains("MoltenVK") + => "MoltenVK", + GraphicsBackend.Vulkan when + Environment.GetEnvironmentVariable("VK_DRIVER_FILES")!.Contains("kosmickrisp") => "KosmicKrisp", + GraphicsBackend.Vulkan => _viewModel.BackendText, GraphicsBackend.OpenGl => "OpenGL", _ => throw new NotImplementedException() }; diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index fb6551ea4..911b51a7f 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -189,6 +189,8 @@ "SettingsTabSystemIgnoreControllerApplet": "Ignore Controller Applet", "SettingsTabGraphics": "Graphics", "SettingsTabGraphicsAPI": "Graphics API", + "SettingsTabTranslationLayer": "Translation Layer (Vulkan):", + "SettingsTabTranslationLayerTooltip": "MoltenVK is a Vulkan Portability implementation, enabling Vulkan applications to run on macOS.\n\nKosmicKrisp is a Vulkan (1.3) conformant Vulkan-on-Metal driver and the eventual successor to MoltenVK.\n\nKosmicKrisp is not yet feature-complete, meaning that games *may* not render correctly or possibly even crash.\n\nUse MoltenVK if unsure.", "SettingsTabGraphicsEnableShaderCache": "Enable Shader Cache", "SettingsTabGraphicsAnisotropicFiltering": "Anisotropic Filtering:", "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index f1bffbecd..0152db0d7 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -289,7 +289,7 @@ namespace Ryujinx.Headless if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) { string preferredGpuId = string.Empty; - Vk api = Vk.GetApi(); + Vk api = VulkanSpbApi.GetApiFromSpb(); if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) { @@ -310,7 +310,8 @@ namespace Ryujinx.Headless api, (instance, _) => new SurfaceKHR((ulong)(vulkanWindow.CreateWindowSurface(instance.Handle))), VulkanWindow.GetRequiredInstanceExtensions, - preferredGpuId); + preferredGpuId, + options.TranslationLayer); } return new OpenGLRenderer(); diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 102e7da84..6ca1b3777 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -12,7 +12,6 @@ using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -74,11 +73,6 @@ namespace Ryujinx.Headless }; } - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - Parser.Default.ParseArguments(args) .WithParsed(options => Load(args, options)) .WithNotParsed(errors => errors.Output()); diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 28eec3f07..118b98fd8 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -131,7 +131,10 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(GraphicsBackend))) GraphicsBackend = configurationState.Graphics.GraphicsBackend; - + + if (NeedsOverride(nameof(TranslationLayer))) + TranslationLayer = configurationState.Graphics.TranslationLayer; + if (NeedsOverride(nameof(AntiAliasing))) AntiAliasing = configurationState.Graphics.AntiAliasing; @@ -370,6 +373,9 @@ namespace Ryujinx.Headless [Option("graphics-backend", Required = false, Default = GraphicsBackend.Vulkan, HelpText = "Change Graphics Backend to use.")] public GraphicsBackend GraphicsBackend { get; set; } + [Option("translation-layer", Required = false, Default = TranslationLayer.MoltenVK, HelpText = "Change Translation Layer to use.")] + public TranslationLayer TranslationLayer { get; set; } + [Option("preferred-gpu-vendor", Required = false, Default = "", HelpText = "When using the Vulkan backend, prefer using the GPU from the specified vendor.")] public string PreferredGPUVendor { get; set; } diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index e7f0ad789..d64a65ca1 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -8,7 +8,6 @@ using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless; using Ryujinx.Modules; using Ryujinx.SDL3.Common; @@ -26,7 +25,6 @@ namespace Ryujinx.Ava { internal partial class Program { - // public static double WindowScaleFactor { get; set; } public static double DesktopScaleFactor { get; set; } = 1.0; public static string Version { get; private set; } @@ -114,11 +112,6 @@ namespace Ryujinx.Ava // Parse arguments CommandLineState.ParseArguments(args); - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); @@ -218,19 +211,32 @@ namespace Ryujinx.Ava UseHardwareAcceleration = ConfigurationState.Instance.EnableHardwareAcceleration.Value; - // Check if graphics backend was overridden - if (CommandLineState.OverrideGraphicsBackend != null) + // Change graphics backend from OpenGL to Vulkan on macOS + if (OperatingSystem.IsMacOS() && (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)) { - if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; - } - else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; - } + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; } + // Check if graphics backend was overridden + if (CommandLineState.OverrideGraphicsBackend is not null) + { + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = CommandLineState.OverrideGraphicsBackend.ToLower() switch + { + "opengl" => GraphicsBackend.OpenGl, + "vulkan" => GraphicsBackend.Vulkan, + _ => ConfigurationState.Instance.Graphics.GraphicsBackend.Value + }; + } + + // Check if translation layer was overridden + if (CommandLineState.OverrideTranslationLayer is not null) + ConfigurationState.Instance.Graphics.TranslationLayer.Value = CommandLineState.OverrideTranslationLayer.ToLower() switch + { + "moltenvk" => TranslationLayer.MoltenVK, + "kosmickrisp" => TranslationLayer.KosmicKrisp, + _ => ConfigurationState.Instance.Graphics.TranslationLayer.Value + }; + // Check if backend threading was overridden if (CommandLineState.OverrideBackendThreading is not null) ConfigurationState.Instance.Graphics.BackendThreading.Value = CommandLineState.OverrideBackendThreading.ToLower() switch diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index f2f144b28..1fc1272f6 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -47,11 +47,12 @@ + + - diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 2a40470f0..2d93f34c7 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -118,6 +118,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + public bool IsKosmicKrispAvailable => OperatingSystem.IsMacOSVersionAtLeast(15); + public bool GameDirectoryChanged { get => _gameDirectoryChanged; @@ -317,12 +319,22 @@ namespace Ryujinx.Ava.UI.ViewModels get; set { - field = value; + field = IsMacOS ? 0 : value; OnPropertyChanged(); OnPropertyChanged(nameof(IsVulkanSelected)); } } + public int TranslationLayerIndex + { + get; + set + { + field = value; + OnPropertyChanged(); + } + } + public int ScalingFilter { get => _scalingFilter; @@ -626,6 +638,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; + TranslationLayerIndex = (int)config.Graphics.TranslationLayer.Value; // Physical devices are queried asynchronously hence the preferred index config value is loaded in LoadAvailableGpus(). EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; @@ -746,6 +759,7 @@ namespace Ryujinx.Ava.UI.ViewModels // Graphics config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; + config.Graphics.TranslationLayer.Value = (TranslationLayer)TranslationLayerIndex; config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex); config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml index 68d269863..b4a515a9e 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -28,7 +28,7 @@ Spacing="10"> - + + + + + + + + - + + + + + + + + +