EXPERIMENTAL: Metal backend (#441)

This is not a continuation of the Metal backend; this is simply bringing
the branch up to date and merging it as-is behind an experiment.

---------

Co-authored-by: Isaac Marovitz <isaacryu@icloud.com>
Co-authored-by: Samuliak <samuliak77@gmail.com>
Co-authored-by: SamoZ256 <96914946+SamoZ256@users.noreply.github.com>
Co-authored-by: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>
Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Co-authored-by: Gabriel A <gab.dark.100@gmail.com>
This commit is contained in:
Evan Husted
2024-12-24 00:55:16 -06:00
committed by GitHub
parent 3094df54dd
commit 852823104f
131 changed files with 14992 additions and 140 deletions

View File

@@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Threading;
using Gommon;
using LibHac.Common;
using LibHac.Ns;
using LibHac.Tools.FsSystem;
@@ -28,6 +29,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Metal;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
@@ -141,6 +143,23 @@ namespace Ryujinx.Ava
public ulong ApplicationId { get; private set; }
public bool ScreenshotRequested { get; set; }
public bool ShouldInitMetal
{
get
{
return OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 &&
(
(
(
ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Auto &&
RendererHost.KnownGreatMetalTitles.ContainsIgnoreCase(ApplicationId.ToString("X16"))
) ||
ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal
)
);
}
}
public AppHost(
RendererHost renderer,
InputManager inputManager,
@@ -893,12 +912,27 @@ namespace Ryujinx.Ava
VirtualFileSystem.ReloadKeySet();
// Initialize Renderer.
IRenderer renderer = ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl
? new OpenGLRenderer()
: VulkanRenderer.Create(
IRenderer renderer;
GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend;
if (ShouldInitMetal)
{
#pragma warning disable CA1416 This call site is reachable on all platforms
// The condition does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem.
renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface);
#pragma warning restore CA1416
}
else if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal))
{
renderer = VulkanRenderer.Create(
ConfigurationState.Instance.Graphics.PreferredGpu,
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions);
}
else
{
renderer = new OpenGLRenderer();
}
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
@@ -1111,10 +1145,11 @@ namespace Ryujinx.Ava
public void InitStatus()
{
_viewModel.BackendText = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
_viewModel.BackendText = RendererHost.Backend switch
{
GraphicsBackend.Vulkan => "Vulkan",
GraphicsBackend.OpenGl => "OpenGL",
GraphicsBackend.Metal => "Metal",
_ => throw new NotImplementedException()
};

View File

@@ -19677,6 +19677,54 @@
"zh_TW": "選擇模擬器將使用的圖形後端。\n\n只要驅動程式是最新的Vulkan 對所有現代顯示卡來說都更好用。Vulkan 還能在所有 GPU 廠商上實現更快的著色器編譯 (減少卡頓)。\n\nOpenGL 在舊式 Nvidia GPU、Linux 上的舊式 AMD GPU 或 VRAM 較低的 GPU 上可能會取得更好的效果,不過著色器編譯的卡頓會更嚴重。\n\n如果不確定請設定為 Vulkan。如果您的 GPU 使用最新的圖形驅動程式也不支援 Vulkan請設定為 OpenGL。"
}
},
{
"ID": "SettingsTabGraphicsBackendAuto",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Auto",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "SettingsTabGraphicsBackendAutoTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Uses Vulkan.\nOn an ARM Mac, and when playing a game that runs well under it, uses the Metal backend.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "SettingsEnableTextureRecompression",
"Translations": {

View File

@@ -198,6 +198,7 @@ namespace Ryujinx.Ava
{
"opengl" => GraphicsBackend.OpenGl,
"vulkan" => GraphicsBackend.Vulkan,
"metal" => GraphicsBackend.Metal,
_ => ConfigurationState.Instance.Graphics.GraphicsBackend
};

View File

@@ -66,6 +66,7 @@
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />

View File

@@ -0,0 +1,20 @@
using SharpMetal.QuartzCore;
using System;
namespace Ryujinx.Ava.UI.Renderer
{
public class EmbeddedWindowMetal : EmbeddedWindow
{
public CAMetalLayer CreateSurface()
{
if (OperatingSystem.IsMacOS())
{
return new CAMetalLayer(MetalLayer);
}
else
{
throw new NotSupportedException();
}
}
}
}

View File

@@ -1,8 +1,10 @@
using Avalonia;
using Avalonia.Controls;
using Gommon;
using Ryujinx.Common.Configuration;
using Ryujinx.UI.Common.Configuration;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Ava.UI.Renderer
{
@@ -17,18 +19,64 @@ namespace Ryujinx.Ava.UI.Renderer
{
InitializeComponent();
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
EmbeddedWindow = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
{
EmbeddedWindow = new EmbeddedWindowOpenGL();
}
else
{
EmbeddedWindow = new EmbeddedWindowVulkan();
}
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
GraphicsBackend.Metal => new EmbeddedWindowMetal(),
GraphicsBackend.Vulkan or GraphicsBackend.Auto => new EmbeddedWindowVulkan(),
_ => throw new NotSupportedException()
};
Initialize();
}
public static readonly string[] KnownGreatMetalTitles =
[
"01006A800016E000", // Smash Ultimate
"0100000000010000", // Super Mario Odyessy
"01008C0016544000", // Sea of Stars
"01005CA01580E000", // Persona 5
"010028600EBDA000", // Mario 3D World
];
public GraphicsBackend Backend =>
EmbeddedWindow switch
{
EmbeddedWindowVulkan => GraphicsBackend.Vulkan,
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
EmbeddedWindowMetal => GraphicsBackend.Metal,
_ => throw new NotImplementedException()
};
public RendererHost(string titleId)
{
InitializeComponent();
switch (ConfigurationState.Instance.Graphics.GraphicsBackend.Value)
{
case GraphicsBackend.Auto:
EmbeddedWindow =
OperatingSystem.IsMacOS() &&
RuntimeInformation.ProcessArchitecture == Architecture.Arm64 &&
KnownGreatMetalTitles.ContainsIgnoreCase(titleId)
? new EmbeddedWindowMetal()
: new EmbeddedWindowVulkan();
break;
case GraphicsBackend.OpenGl:
EmbeddedWindow = new EmbeddedWindowOpenGL();
break;
case GraphicsBackend.Metal:
EmbeddedWindow = new EmbeddedWindowMetal();
break;
case GraphicsBackend.Vulkan:
EmbeddedWindow = new EmbeddedWindowVulkan();
break;
}
Initialize();
}
private void Initialize()
{
EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated;

View File

@@ -1938,7 +1938,7 @@ namespace Ryujinx.Ava.UI.ViewModels
PrepareLoadScreen();
RendererHostControl = new RendererHost();
RendererHostControl = new RendererHost(application.Id.ToString("X16"));
AppHost = new AppHost(
RendererHostControl,

View File

@@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
public bool GameDirectoryChanged
{
@@ -252,7 +252,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
public bool IsVulkanSelected =>
GraphicsBackendIndex == 1 || (GraphicsBackendIndex == 0 && !OperatingSystem.IsMacOS());
public bool UseHypervisor { get; set; }
public bool DisableP2P { get; set; }
@@ -432,7 +433,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (devices.Length == 0)
{
IsVulkanAvailable = false;
GraphicsBackendIndex = 1;
GraphicsBackendIndex = 2;
}
else
{

View File

@@ -69,7 +69,7 @@
</ComboBox>
</StackPanel>
<CheckBox IsChecked="{Binding UseHypervisor}"
IsVisible="{Binding IsHypervisorAvailable}"
IsVisible="{Binding IsAppleSiliconMac}"
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemUseHypervisor}"
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />

View File

@@ -33,16 +33,24 @@
ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}"
Text="{ext:Locale SettingsTabGraphicsBackend}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}"
SelectedIndex="{Binding GraphicsBackendIndex}">
<ComboBox
Name="GraphicsBackendSelector"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}"
SelectedIndex="{Binding GraphicsBackendIndex}">
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}" ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendAutoTooltip}" >
<TextBlock Text="{ext:Locale SettingsTabGraphicsBackendAuto}" />
</ComboBoxItem>
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
<TextBlock Text="Vulkan" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
<TextBlock Text="OpenGL" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsAppleSiliconMac}">
<TextBlock Text="Metal (ARM Mac only, Experimental)" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">

View File

@@ -56,34 +56,34 @@ namespace Ryujinx.Ava.UI.Windows
{
switch (navItem.Tag.ToString())
{
case "UiPage":
case nameof(UiPage):
UiPage.ViewModel = ViewModel;
NavPanel.Content = UiPage;
break;
case "InputPage":
case nameof(InputPage):
NavPanel.Content = InputPage;
break;
case "HotkeysPage":
case nameof(HotkeysPage):
NavPanel.Content = HotkeysPage;
break;
case "SystemPage":
case nameof(SystemPage):
SystemPage.ViewModel = ViewModel;
NavPanel.Content = SystemPage;
break;
case "CpuPage":
case nameof(CpuPage):
NavPanel.Content = CpuPage;
break;
case "GraphicsPage":
case nameof(GraphicsPage):
NavPanel.Content = GraphicsPage;
break;
case "AudioPage":
case nameof(AudioPage):
NavPanel.Content = AudioPage;
break;
case "NetworkPage":
case nameof(NetworkPage):
NetworkPage.ViewModel = ViewModel;
NavPanel.Content = NetworkPage;
break;
case "LoggingPage":
case nameof(LoggingPage):
NavPanel.Content = LoggingPage;
break;
default: