diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 95b0aa255..0c3451b1e 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -73,7 +73,7 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }} - name: Build - run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ steps.version_info.outputs.result }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER + run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ steps.version_info.outputs.result }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER -p:UseSharedCompilation=false - name: Test uses: actions/unstable-commands@v1 @@ -84,7 +84,7 @@ jobs: if: matrix.platform.name != 'linux-arm64' - name: Publish Ryujinx - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.result }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.result }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER -p:UseSharedCompilation=false src/Ryujinx --self-contained if: forgejo.event_name == 'pull_request' - name: Packing Windows builds diff --git a/.forgejo/workflows/canary.yml b/.forgejo/workflows/canary.yml index 771575c4b..da27b54c0 100644 --- a/.forgejo/workflows/canary.yml +++ b/.forgejo/workflows/canary.yml @@ -76,7 +76,7 @@ jobs: - name: Publish run: | - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded -p:UseSharedCompilation=false src/Ryujinx --self-contained - name: Packing Windows builds if: contains(matrix.platform.name, 'win') diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 4f9672470..ad011be60 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -73,7 +73,7 @@ jobs: - name: Publish run: | - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx -p:UseSharedCompilation=false --self-contained - name: Packing Windows builds if: contains(matrix.platform.name, 'win') diff --git a/Directory.Packages.props b/Directory.Packages.props index 469db9f71..bb340a061 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -74,9 +74,11 @@ + + diff --git a/assets/Locales/Root.json b/assets/Locales/Root.json index 447009aba..f262f35ac 100644 --- a/assets/Locales/Root.json +++ b/assets/Locales/Root.json @@ -4225,6 +4225,56 @@ "zh_TW": "圖形 API & 優化" } }, + { + "ID": "SettingsTabGraphicsEnableKosmicKrisp", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Enable KosmicKrisp (macOS 26+)", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabGraphicsEnableKosmicKrispTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Enables the KosmicKrisp Vulkan-on-Metal translation layer, which is baked into Mesa 26.0+.\n\nOverrides MoltenVK.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabGraphicsEnableShaderCache", "Translations": { @@ -16300,6 +16350,56 @@ "zh_TW": "選擇模擬器將使用的圖形後端。\n\n只要驅動程式是最新的,Vulkan 對所有現代顯示卡來說都更好用。Vulkan 還能在所有 GPU 廠商上實現更快的著色器編譯 (減少卡頓)。\n\nOpenGL 在舊式 Nvidia GPU、Linux 上的舊式 AMD GPU 或 VRAM 較低的 GPU 上可能會取得更好的效果,不過著色器編譯的卡頓會更嚴重。\n\n如果不確定,請設定為 Vulkan。如果您的 GPU 使用最新的圖形驅動程式也不支援 Vulkan,請設定為 OpenGL。" } }, + { + "ID": "SettingsTabTranslationLayer", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Translation Layer:", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabTranslationLayerTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Select the translation layer that will be used with Vulkan on macOS.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsEnableTextureRecompression", "Translations": { diff --git a/distribution/macos/construct_universal_dylib.py b/distribution/macos/construct_universal_dylib.py index 5d9321860..69462d1b2 100644 --- a/distribution/macos/construct_universal_dylib.py +++ b/distribution/macos/construct_universal_dylib.py @@ -47,14 +47,12 @@ def get_new_name( input_component = str(input_dylib_path).replace(str(input_directory), "")[1:] return Path(os.path.join(output_directory, input_component)) - -def is_fat_file(dylib_path: Path) -> str: - res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode( - "utf-8" - ) - - return not res.split("\n")[0].startswith("Non-fat file") - +def get_archs(dylib_path: Path) -> list[str]: + 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: + return res.split("are:")[-1].strip().split() def construct_universal_dylib( arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path @@ -69,11 +67,12 @@ def construct_universal_dylib( os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path ) else: - if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists(): - with open(output_dylib_path, "wb") as dst: - with open(arm64_input_dylib_path, "rb") as src: - dst.write(src.read()) - else: + arm64_archs = get_archs(arm64_input_dylib_path) + x86_64_archs = get_archs(x86_64_input_dylib_path) if x86_64_input_dylib_path.exists() else [] + + if "arm64" in arm64_archs and "x86_64" in arm64_archs: + shutil.copy2(arm64_input_dylib_path, output_dylib_path) + elif x86_64_archs: subprocess.check_call( [ LIPO, diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index b90848334..7b449dd2c 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -14,18 +14,22 @@ mkdir "$APP_BUNDLE_DIRECTORY/Contents/Frameworks" mkdir "$APP_BUNDLE_DIRECTORY/Contents/MacOS" mkdir "$APP_BUNDLE_DIRECTORY/Contents/Resources" -# Copy executable and nsure executable can be executed +# Copy executable and ensure executable can be executed 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 028486f51..97a3e0397 100755 --- a/distribution/macos/create_macos_build_ava.sh +++ b/distribution/macos/create_macos_build_ava.sh @@ -48,7 +48,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 @@ -77,6 +77,11 @@ rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" # Make its 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/libarmeilleure-jitsupport.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libarmeilleure-jitsupport.dylib" +cp "$ARM64_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" +cp "$ARM64_APP_BUNDLE/Contents/Frameworks/libvulkan.1.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libvulkan.1.dylib" + if ! [ -x "$(command -v lipo)" ]; then if ! [ -x "$(command -v llvm-lipo-17)" ]; diff --git a/distribution/macos/create_macos_pr_build_ava.sh b/distribution/macos/create_macos_pr_build_ava.sh index 206f2eec9..fb9fdb3ae 100644 --- a/distribution/macos/create_macos_pr_build_ava.sh +++ b/distribution/macos/create_macos_pr_build_ava.sh @@ -41,7 +41,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 @@ -70,6 +70,11 @@ rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" # Make its 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/libarmeilleure-jitsupport.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libarmeilleure-jitsupport.dylib" +cp "$ARM64_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libvulkan_kosmickrisp.dylib" +cp "$ARM64_APP_BUNDLE/Contents/Frameworks/libvulkan.1.dylib" "$UNIVERSAL_APP_BUNDLE/Contents/Frameworks/libvulkan.1.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..76cec2757 --- /dev/null +++ b/distribution/macos/vulkan/icd.d/libkosmickrisp_icd.json @@ -0,0 +1,7 @@ +{ + "ICD": { + "api_version": "1.3.348", + "library_path": "../../../Frameworks/libvulkan_kosmickrisp.dylib" + }, + "file_format_version": "1.0.1" +} \ No newline at end of file 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/KosmicKrisp/KKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/KosmicKrisp/KKInitialization.cs new file mode 100644 index 000000000..ba1a76fa5 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/KosmicKrisp/KKInitialization.cs @@ -0,0 +1,28 @@ +using Ryujinx.Common.Logging; +using System; +using System.IO; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Vulkan.KosmicKrisp +{ + [SupportedOSPlatform("macos")] + public static class KKInitialization + { + public static void Initialize() + { + string contentsDir = Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd('/'))!; + string basePath = Path.Combine(contentsDir, "Resources"); + string icdPath = Path.Combine(basePath, "vulkan", "icd.d", "libkosmickrisp_icd.json"); + + if (!File.Exists(icdPath)) + throw new FileNotFoundException("KosmicKrisp ICD JSON not found", icdPath); + + Logger.Notice.Print(LogClass.Application, + $"KKInitialization.Initialize() called, VK_DRIVER_FILES will be set to: {icdPath}"); + + Environment.SetEnvironmentVariable("VK_DRIVER_FILES", icdPath); + + Console.WriteLine($"[KKInit] VK_DRIVER_FILES just set to: {Environment.GetEnvironmentVariable("VK_DRIVER_FILES")}"); + } + } +} 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 index 4f5dbf6b9..2ac4a9928 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -1,52 +1,33 @@ -using Silk.NET.Core.Loader; -using Silk.NET.Vulkan; +using Ryujinx.Common.Logging; using System; -using System.Runtime.InteropServices; +using System.IO; using System.Runtime.Versioning; namespace Ryujinx.Graphics.Vulkan.MoltenVK { [SupportedOSPlatform("macos")] - public static partial class MVKInitialization + public static 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() { - nint configSize = (nint)Marshal.SizeOf(); + 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"); - vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize); + string contentsDir = Path.GetDirectoryName(AppContext.BaseDirectory.TrimEnd('/'))!; + string basePath = Path.Combine(contentsDir, "Resources"); + string icdPath = Path.Combine(basePath, "vulkan", "icd.d", "MoltenVK_icd.json"); - config.UseMetalArgumentBuffers = true; + if (!File.Exists(icdPath)) + throw new FileNotFoundException("MoltenVK ICD JSON not found", icdPath); - config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE; - config.SynchronousQueueSubmits = false; + Logger.Notice.Print(LogClass.Application, + $"MVKInitialization.Initialize() called, VK_DRIVER_FILES will be set to: {icdPath}"); - config.ResumeLostDevice = true; + Environment.SetEnvironmentVariable("VK_DRIVER_FILES", icdPath); - 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); + Console.WriteLine($"[MVKInit] VK_DRIVER_FILES just set to: {Environment.GetEnvironmentVariable("VK_DRIVER_FILES")}"); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index 370430aba..e3817d204 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs index 550ba0221..d5bbef588 100644 --- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -86,6 +86,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/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 9cd8f90d7..4c98cfc81 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; @@ -195,13 +195,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 = []; @@ -392,6 +385,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; @@ -500,6 +509,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; + PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8; if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8")) 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 d2170a682..90c9ed978 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -5,7 +5,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; @@ -98,6 +97,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; } @@ -121,19 +121,9 @@ namespace Ryujinx.Graphics.Vulkan Textures = []; Samplers = []; - // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. - if (IsMoltenVk = OperatingSystem.IsMacOS()) - MVKInitialization.Initialize(); - SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14); } - public static VulkanRenderer Create( - string preferredGpuId, - Func getSurface, - Func getRequiredExtensions - ) => new(Vk.GetApi(), getSurface, getRequiredExtensions, preferredGpuId); - private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex) { FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice); @@ -408,6 +398,10 @@ namespace Ryujinx.Graphics.Vulkan IsQualcommProprietary = hasDriverProperties && driverProperties.DriverID == DriverId.QualcommProprietary; + IsMoltenVk = hasDriverProperties && driverProperties.DriverID == DriverId.Moltenvk; + + IsKosmicKrisp = hasDriverProperties && driverProperties.DriverID == DriverId.MesaKosmickrisp; + ulong minResourceAlignment = Math.Max( Math.Max( properties.Limits.MinStorageBufferOffsetAlignment, @@ -834,14 +828,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/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index ff7e56c52..5cb332d84 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -13,6 +13,8 @@ using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Vulkan.KosmicKrisp; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.HLE; using Ryujinx.Input; using Ryujinx.Input.SDL3; @@ -185,8 +187,22 @@ namespace Ryujinx.Headless { if (options.GraphicsBackend == GraphicsBackend.Vulkan && window is VulkanWindow vulkanWindow) { + // 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()) + { + if (options.TranslationLayer == TranslationLayer.MoltenVK) + { + MVKInitialization.Initialize(); + } + else if (options.TranslationLayer == TranslationLayer.KosmicKrisp) + { + KKInitialization.Initialize(); + } + } + string preferredGpuId = string.Empty; - Vk api = Vk.GetApi(); + Vk api = VulkanSpbApi.GetApiFromSpb(); if (!string.IsNullOrEmpty(options.PreferredGPUVendor)) { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index a6ff5da57..87e01ec2e 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -77,11 +77,6 @@ namespace Ryujinx.Headless }; } - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - Parser.Default.ParseArguments(args) .WithParsed(options => Load(args, options)) .WithNotParsed(errors => diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index a2dd6e4d3..5cb88c501 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -136,6 +136,9 @@ 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; @@ -403,6 +406,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 5011cd664..5e1b3350b 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -20,6 +20,8 @@ using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; using Ryujinx.Graphics.RenderDocApi; using Ryujinx.Graphics.Vulkan.MoltenVK; +using Ryujinx.Graphics.Vulkan.KosmicKrisp; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless; using Ryujinx.SDL3.Common; using System; @@ -237,11 +239,6 @@ namespace Ryujinx.Ava // Parse arguments CommandLineState.ParseArguments(args); - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - } - // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); @@ -366,11 +363,22 @@ namespace Ryujinx.Ava // 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 { - "opengl" => GraphicsBackend.OpenGl, - "vulkan" => GraphicsBackend.Vulkan, - _ => ConfigurationState.Instance.Graphics.GraphicsBackend + "moltenvk" => TranslationLayer.MoltenVK, + "kosmickrisp" => TranslationLayer.KosmicKrisp, + _ => ConfigurationState.Instance.Graphics.TranslationLayer.Value }; // Check if backend threading was overridden diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj index 6503d57f5..1dae5de70 100644 --- a/src/Ryujinx/Ryujinx.csproj +++ b/src/Ryujinx/Ryujinx.csproj @@ -28,14 +28,14 @@ true partial - + - + true @@ -64,6 +64,8 @@ + + @@ -71,9 +73,9 @@ - + - + diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs index 650ea5ae9..8c1ba3e2f 100644 --- a/src/Ryujinx/Systems/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -35,11 +35,14 @@ using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Vulkan.KosmicKrisp; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Input; using Ryujinx.Input.HLE; +using Silk.NET.Vulkan; using SkiaSharp; using SPB.Graphics.Vulkan; using System; @@ -975,16 +978,43 @@ namespace Ryujinx.Ava.Systems VirtualFileSystem.ReloadKeySet(); // Initialize Renderer. - GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; + IRenderer renderer; - IRenderer renderer = backend switch + if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { - GraphicsBackend.Vulkan => VulkanRenderer.Create( - ConfigurationState.Instance.Graphics.PreferredGpu, - (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, - VulkanHelper.GetRequiredInstanceExtensions), - _ => new OpenGLRenderer() - }; + Logger.Notice.Print(LogClass.Application, + $"TranslationLayer at init: {ConfigurationState.Instance.Graphics.TranslationLayer.Value}"); + + // 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()) + { + TranslationLayer translationLayer = ConfigurationState.Instance.Graphics.TranslationLayer.Value; + if (translationLayer == TranslationLayer.MoltenVK) + { + MVKInitialization.Initialize(); + } + else if (translationLayer == TranslationLayer.KosmicKrisp) + { + KKInitialization.Initialize(); + } + } + + renderer = new VulkanRenderer( + VulkanSpbApi.GetApiFromSpb(), + ((EmbeddedWindowVulkan)RendererHost.EmbeddedWindow).CreateSurface, + VulkanHelper.GetRequiredInstanceExtensions, + ConfigurationState.Instance.Graphics.PreferredGpu.Value); + + Logger.Notice.Print(LogClass.Application, + $"TranslationLayer just set to: {ConfigurationState.Instance.Graphics.TranslationLayer.Value}"); + + Console.WriteLine($"VK_DRIVER_FILES post-init: {Environment.GetEnvironmentVariable("VK_DRIVER_FILES")}"); + } + else + { + renderer = new OpenGLRenderer(); + } // Initialize Configuration. Device = new Switch(ConfigurationState.Instance.CreateHleConfiguration() diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs index 7928a6194..4494b996e 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 73; + public const int CurrentVersion = 74; /// /// Version of the configuration file format @@ -446,6 +446,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// public GraphicsBackend GraphicsBackend { get; set; } + /// + /// Vulkan translation layer + /// + public TranslationLayer TranslationLayer { get; set; } + /// /// Preferred GPU /// diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs index 59b1f5804..c9d3ee315 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs @@ -81,6 +81,7 @@ namespace Ryujinx.Ava.Systems.Configuration Graphics.ShadersDumpPath.Value = cff.GraphicsShadersDumpPath; Graphics.BackendThreading.Value = cff.BackendThreading; Graphics.GraphicsBackend.Value = cff.GraphicsBackend; + Graphics.TranslationLayer.Value = cff.TranslationLayer; Graphics.PreferredGpu.Value = cff.PreferredGpu; Graphics.AntiAliasing.Value = cff.AntiAliasing; Graphics.ScalingFilter.Value = cff.ScalingFilter; @@ -542,7 +543,9 @@ namespace Ryujinx.Ava.Systems.Configuration if (cff.AudioBackend is AudioBackend.SDL2) cff.AudioBackend = AudioBackend.SDL3; }), - (72, static cff => cff.GCLowLatency = false) + (72, static cff => cff.GCLowLatency = false), + // skip 73 + (74, static cff => cff.TranslationLayer = TranslationLayer.MoltenVK) ); } } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs index 6af7a2383..c368fe869 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs @@ -634,6 +634,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// public ReactiveObject GraphicsBackend { get; private set; } + /// + /// Vulkan translation layer + /// + public ReactiveObject TranslationLayer { get; private set; } + /// /// Applies anti-aliasing to the renderer. /// @@ -679,6 +684,8 @@ namespace Ryujinx.Ava.Systems.Configuration EnableTextureRecompression.LogChangesToValue(nameof(EnableTextureRecompression)); GraphicsBackend = new ReactiveObject(); GraphicsBackend.LogChangesToValue(nameof(GraphicsBackend)); + TranslationLayer = new ReactiveObject(); + TranslationLayer.LogChangesToValue(nameof(TranslationLayer)); PreferredGpu = new ReactiveObject(); PreferredGpu.LogChangesToValue(nameof(PreferredGpu)); EnableMacroHLE = new ReactiveObject(); diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs index bcd8e50ca..ad83de3eb 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs @@ -147,6 +147,7 @@ namespace Ryujinx.Ava.Systems.Configuration AllowDuplicateDeviceAssignment = Hid.AllowDuplicateDeviceAssignment, RainbowSpeed = Hid.RainbowSpeed, GraphicsBackend = Graphics.GraphicsBackend, + TranslationLayer = Graphics.TranslationLayer, PreferredGpu = Graphics.PreferredGpu, MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, MultiplayerMode = Multiplayer.Mode, @@ -173,6 +174,7 @@ namespace Ryujinx.Ava.Systems.Configuration Graphics.MaxAnisotropy.Value = -1.0f; Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; Graphics.GraphicsBackend.Value = DefaultGraphicsBackend(); + Graphics.TranslationLayer.Value = TranslationLayer.MoltenVK; Graphics.PreferredGpu.Value = string.Empty; Graphics.ShadersDumpPath.Value = string.Empty; Logger.EnableDebug.Value = false; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 44caffb8d..6751ec3fc 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -170,6 +170,8 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); + public bool IsKosmicKrispAvailable => OperatingSystem.IsMacOSVersionAtLeast(15); + public bool EnableDiscordIntegration { get; set; } public bool ShowConfirmExit { get; set; } public bool IgnoreApplet { get; set; } @@ -308,8 +310,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsCustomResolutionScaleActive => _resolutionScale == 4; public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr; - public bool IsVulkanSelected => - GraphicsBackendIndex == 1 || (GraphicsBackendIndex == 0 && !OperatingSystem.IsMacOS()); + public bool IsVulkanSelected => GraphicsBackendIndex == 0; public bool UseHypervisor { get; set; } public bool GCLowLatency { get; set; } public bool DisableP2P { get; set; } @@ -367,6 +368,17 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public int TranslationLayerIndex + { + get; + set + { + field = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(IsVulkanSelected)); + } + } + public int ScalingFilter { get => _scalingFilter; @@ -573,7 +585,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (devices.Length == 0) { IsVulkanAvailable = false; - GraphicsBackendIndex = 2; + GraphicsBackendIndex = 1; } else { @@ -722,6 +734,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; @@ -839,6 +852,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval; config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval; 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 22c7acfa2..ceeb1a7cd 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsGraphicsView.axaml @@ -28,7 +28,7 @@ Spacing="10"> - + + + + + + + + = args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + OverrideTranslationLayer = args[++i]; + break; case "--backend-threading": if (i + 1 >= args.Length) {