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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
131 changed files with 14992 additions and 140 deletions

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: