UI: Add Metal surface creation for MoltenVK (#3980)

* Initial implementation of metal surface across UIs

* Fix SDL2 on windows

* Update Ryujinx/Ryujinx.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Address Feedback

Co-authored-by: Mary-nyan <thog@protonmail.com>
This commit is contained in:
riperiperi 2022-12-06 22:00:25 +00:00 committed by GitHub
parent d3709a753f
commit e211c3f00a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 495 additions and 63 deletions

View file

@ -125,7 +125,7 @@ namespace Ryujinx.Ava
_inputManager = inputManager; _inputManager = inputManager;
_accountManager = accountManager; _accountManager = accountManager;
_userChannelPersistence = userChannelPersistence; _userChannelPersistence = userChannelPersistence;
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" }; _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" };
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;

View file

@ -0,0 +1,127 @@
using System;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
using Avalonia;
namespace Ryujinx.Ava.Ui.Helper
{
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
[SupportedOSPlatform("macos")]
static class MetalHelper
{
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
private struct Selector
{
public readonly IntPtr NativePtr;
public unsafe Selector(string value)
{
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
byte* data = stackalloc byte[size];
fixed (char* pValue = value)
{
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
}
NativePtr = sel_registerName(data);
}
public static implicit operator Selector(string value) => new Selector(value);
}
private static unsafe IntPtr GetClass(string value)
{
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
byte* data = stackalloc byte[size];
fixed (char* pValue = value)
{
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
}
return objc_getClass(data);
}
private struct NSPoint
{
public double X;
public double Y;
public NSPoint(double x, double y)
{
X = x;
Y = y;
}
}
private struct NSRect
{
public NSPoint Pos;
public NSPoint Size;
public NSRect(double x, double y, double width, double height)
{
Pos = new NSPoint(x, y);
Size = new NSPoint(width, height);
}
}
public static IntPtr GetMetalLayer(out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
{
// Create a new CAMetalLayer.
IntPtr layerClass = GetClass("CAMetalLayer");
IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
objc_msgSend(metalLayer, "init");
// Create a child NSView to render into.
IntPtr nsViewClass = GetClass("NSView");
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
objc_msgSend(child, "init", new NSRect(0, 0, 0, 0));
// Make its renderer our metal layer.
objc_msgSend(child, "setWantsLayer:", (byte)1);
objc_msgSend(child, "setLayer:", metalLayer);
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
// Ensure the scale factor is up to date.
updateBounds = (Rect rect) => {
objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
};
nsView = child;
return metalLayer;
}
public static void DestroyMetalLayer(IntPtr nsView, IntPtr metalLayer)
{
// TODO
}
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr sel_registerName(byte* data);
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr objc_getClass(byte* data);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, double value);
[DllImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
}
}

View file

@ -23,6 +23,7 @@ namespace Ryujinx.Ava
internal class Program internal class Program
{ {
public static double WindowScaleFactor { get; set; } public static double WindowScaleFactor { get; set; }
public static double DesktopScaleFactor { get; set; } = 1.0;
public static string Version { get; private set; } public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; } public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; } public static bool PreviewerDetached { get; private set; }

View file

@ -31,8 +31,10 @@
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" /> <PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageReference Include="OpenTK.Core" Version="4.7.5" /> <PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.osx" Version="5.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" /> <PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />

View file

@ -2,11 +2,10 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Ava.Ui.Helper;
using SPB.Graphics; using SPB.Graphics;
using SPB.Platform; using SPB.Platform;
using SPB.Platform.GLX; using SPB.Platform.GLX;
using SPB.Platform.X11;
using SPB.Windowing;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@ -23,6 +22,10 @@ namespace Ryujinx.Ava.Ui.Controls
protected GLXWindow X11Window { get; set; } protected GLXWindow X11Window { get; set; }
protected IntPtr WindowHandle { get; set; } protected IntPtr WindowHandle { get; set; }
protected IntPtr X11Display { get; set; } protected IntPtr X11Display { get; set; }
protected IntPtr NsView { get; set; }
protected IntPtr MetalLayer { get; set; }
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
public event EventHandler<IntPtr> WindowCreated; public event EventHandler<IntPtr> WindowCreated;
public event EventHandler<Size> SizeChanged; public event EventHandler<Size> SizeChanged;
@ -58,6 +61,7 @@ namespace Ryujinx.Ava.Ui.Controls
private void StateChanged(Rect rect) private void StateChanged(Rect rect)
{ {
SizeChanged?.Invoke(this, rect.Size); SizeChanged?.Invoke(this, rect.Size);
_updateBoundsCallback?.Invoke(rect);
} }
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
@ -70,6 +74,11 @@ namespace Ryujinx.Ava.Ui.Controls
{ {
return CreateWin32(parent); return CreateWin32(parent);
} }
else if (OperatingSystem.IsMacOS())
{
return CreateMacOs(parent);
}
return base.CreateNativeControlCore(parent); return base.CreateNativeControlCore(parent);
} }
@ -85,6 +94,10 @@ namespace Ryujinx.Ava.Ui.Controls
{ {
DestroyWin32(control); DestroyWin32(control);
} }
else if (OperatingSystem.IsMacOS())
{
DestroyMacOS();
}
else else
{ {
base.DestroyNativeControlCore(control); base.DestroyNativeControlCore(control);
@ -187,6 +200,16 @@ namespace Ryujinx.Ava.Ui.Controls
return DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam); return DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam);
} }
[SupportedOSPlatform("macos")]
IPlatformHandle CreateMacOs(IPlatformHandle parent)
{
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
NsView = nsView;
return new PlatformHandle(nsView, "NSView");
}
void DestroyLinux() void DestroyLinux()
{ {
X11Window?.Dispose(); X11Window?.Dispose();
@ -198,5 +221,11 @@ namespace Ryujinx.Ava.Ui.Controls
DestroyWindow(handle.Handle); DestroyWindow(handle.Handle);
UnregisterClass(_className, GetModuleHandle(null)); UnregisterClass(_className, GetModuleHandle(null));
} }
[SupportedOSPlatform("macos")]
void DestroyMacOS()
{
MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
}
} }
} }

View file

@ -3,6 +3,7 @@ using Ryujinx.Ava.Ui.Controls;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using SPB.Graphics.Vulkan; using SPB.Graphics.Vulkan;
using SPB.Platform.GLX; using SPB.Platform.GLX;
using SPB.Platform.Metal;
using SPB.Platform.Win32; using SPB.Platform.Win32;
using SPB.Platform.X11; using SPB.Platform.X11;
using SPB.Windowing; using SPB.Windowing;
@ -37,6 +38,10 @@ namespace Ryujinx.Ava.Ui
{ {
_window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle)); _window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle));
} }
else if (OperatingSystem.IsMacOS())
{
_window = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer));
}
else else
{ {
throw new PlatformNotSupportedException(); throw new PlatformNotSupportedException();

View file

@ -108,6 +108,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
} }
} }
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
public bool DirectoryChanged public bool DirectoryChanged
{ {
get => _directoryChanged; get => _directoryChanged;

View file

@ -154,6 +154,12 @@ namespace Ryujinx.Ava.Ui.Windows
} }
} }
protected override void HandleScalingChanged(double scale)
{
Program.DesktopScaleFactor = scale;
base.HandleScalingChanged(scale);
}
public void Application_Opened(object sender, ApplicationOpenedEventArgs args) public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
{ {
if (args.Application != null) if (args.Application != null)

View file

@ -540,7 +540,7 @@
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}"> <ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
<TextBlock Text="Vulkan" /> <TextBlock Text="Vulkan" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
<TextBlock Text="OpenGL" /> <TextBlock Text="OpenGL" />
</ComboBoxItem> </ComboBoxItem>
</ComboBox> </ComboBox>

View file

@ -45,7 +45,14 @@ namespace Ryujinx.Common.Configuration
public static void Initialize(string baseDirPath) public static void Initialize(string baseDirPath)
{ {
string userProfilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir); string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (appDataPath.Length == 0)
{
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}
string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir); string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
if (Directory.Exists(portablePath)) if (Directory.Exists(portablePath))

View file

@ -1,4 +1,4 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
ExtConditionalRendering.ExtensionName, ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName, ExtExtendedDynamicState.ExtensionName,
ExtTransformFeedback.ExtensionName,
KhrDrawIndirectCount.ExtensionName, KhrDrawIndirectCount.ExtensionName,
KhrPushDescriptor.ExtensionName, KhrPushDescriptor.ExtensionName,
"VK_EXT_custom_border_color", "VK_EXT_custom_border_color",
@ -36,8 +37,7 @@ namespace Ryujinx.Graphics.Vulkan
public static string[] RequiredExtensions { get; } = new string[] public static string[] RequiredExtensions { get; } = new string[]
{ {
KhrSwapchain.ExtensionName, KhrSwapchain.ExtensionName
ExtTransformFeedback.ExtensionName
}; };
private static string[] _excludedMessages = new string[] private static string[] _excludedMessages = new string[]
@ -382,12 +382,12 @@ namespace Ryujinx.Graphics.Vulkan
DepthClamp = true, DepthClamp = true,
DualSrcBlend = true, DualSrcBlend = true,
FragmentStoresAndAtomics = true, FragmentStoresAndAtomics = true,
GeometryShader = true, GeometryShader = supportedFeatures.GeometryShader,
ImageCubeArray = true, ImageCubeArray = true,
IndependentBlend = true, IndependentBlend = true,
LogicOp = true, LogicOp = supportedFeatures.LogicOp,
MultiViewport = true, MultiViewport = true,
PipelineStatisticsQuery = true, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
SamplerAnisotropy = true, SamplerAnisotropy = true,
ShaderClipDistance = true, ShaderClipDistance = true,
ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderFloat64 = supportedFeatures.ShaderFloat64,

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -11,7 +11,6 @@ using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{ {
@ -68,8 +67,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{ {
int ryujinxLogoSize = 32; int ryujinxLogoSize = 32;
Stream logoStream = EmbeddedResources.GetStream("Ryujinx.Ui.Common/Resources/Logo_Ryujinx.png"); string ryujinxIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Logo_Ryujinx.png";
_ryujinxLogo = LoadResource(logoStream, ryujinxLogoSize, ryujinxLogoSize); _ryujinxLogo = LoadResource(Assembly.GetExecutingAssembly(), ryujinxIconPath, ryujinxLogoSize, ryujinxLogoSize);
string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png"; string padAcceptIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnA.png";
string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png"; string padCancelIconPath = "Ryujinx.HLE.HOS.Applets.SoftwareKeyboard.Resources.Icon_BtnB.png";
@ -117,7 +116,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
uiThemeFontFamily, uiThemeFontFamily,
"Liberation Sans", "Liberation Sans",
"FreeSans", "FreeSans",
"DejaVu Sans" "DejaVu Sans",
"Lucida Grande"
}; };
foreach (string fontFamily in availableFonts) foreach (string fontFamily in availableFonts)

View file

@ -36,6 +36,7 @@
<ItemGroup> <ItemGroup>
<None Remove="Homebrew.npdm" /> <None Remove="Homebrew.npdm" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" /> <None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" /> <None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" /> <None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" />
@ -44,6 +45,7 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Homebrew.npdm" /> <EmbeddedResource Include="Homebrew.npdm" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" /> <EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" /> <EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnB.png" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" /> <EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_KeyF6.png" />

View file

@ -77,6 +77,26 @@ namespace Ryujinx.Headless.SDL2
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient); _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
_userChannelPersistence = new UserChannelPersistence(); _userChannelPersistence = new UserChannelPersistence();
if (OperatingSystem.IsMacOS())
{
AutoResetEvent invoked = new AutoResetEvent(false);
// MacOS must perform SDL polls from the main thread.
Ryujinx.SDL2.Common.SDL2Driver.MainThreadDispatcher = (Action action) =>
{
invoked.Reset();
WindowBase.QueueMainThreadAction(() =>
{
action();
invoked.Set();
});
invoked.WaitOne();
};
}
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
GraphicsConfig.EnableShaderCache = true; GraphicsConfig.EnableShaderCache = true;

View file

@ -12,7 +12,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="OpenTK.Core" Version="4.7.5" /> <PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.osx" Version="5.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.SDL2.Common;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static SDL2.SDL; using static SDL2.SDL;
@ -26,9 +27,18 @@ namespace Ryujinx.Headless.SDL2.Vulkan
MouseDriver.SetClientSize(DefaultWidth, DefaultHeight); MouseDriver.SetClientSize(DefaultWidth, DefaultHeight);
} }
private void BasicInvoke(Action action)
{
action();
}
public unsafe IntPtr CreateWindowSurface(IntPtr instance) public unsafe IntPtr CreateWindowSurface(IntPtr instance)
{ {
if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out ulong surfaceHandle) == SDL_bool.SDL_FALSE) ulong surfaceHandle = 0;
Action createSurface = () =>
{
if (SDL_Vulkan_CreateSurface(WindowHandle, instance, out surfaceHandle) == SDL_bool.SDL_FALSE)
{ {
string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\""; string errorMessage = $"SDL_Vulkan_CreateSurface failed with error \"{SDL_GetError()}\"";
@ -36,6 +46,16 @@ namespace Ryujinx.Headless.SDL2.Vulkan
throw new Exception(errorMessage); throw new Exception(errorMessage);
} }
};
if (SDL2Driver.MainThreadDispatcher != null)
{
SDL2Driver.MainThreadDispatcher(createSurface);
}
else
{
createSurface();
}
return (IntPtr)surfaceHandle; return (IntPtr)surfaceHandle;
} }

View file

@ -11,6 +11,7 @@ using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@ -26,6 +27,13 @@ namespace Ryujinx.Headless.SDL2
private const SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN; private const SDL_WindowFlags DefaultFlags = SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL_WindowFlags.SDL_WINDOW_RESIZABLE | SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS | SDL_WindowFlags.SDL_WINDOW_SHOWN;
private const int TargetFps = 60; private const int TargetFps = 60;
private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();
public static void QueueMainThreadAction(Action action)
{
MainThreadActions.Enqueue(action);
}
public NpadManager NpadManager { get; } public NpadManager NpadManager { get; }
public TouchScreenManager TouchScreenManager { get; } public TouchScreenManager TouchScreenManager { get; }
public Switch Device { get; private set; } public Switch Device { get; private set; }
@ -168,6 +176,14 @@ namespace Ryujinx.Headless.SDL2
public void Render() public void Render()
{ {
InitializeWindowRenderer();
Device.Gpu.Renderer.Initialize(_glLogLevel);
InitializeRenderer();
_gpuVendorName = GetGpuVendorName();
Device.Gpu.Renderer.RunLoop(() => Device.Gpu.Renderer.RunLoop(() =>
{ {
Device.Gpu.SetGpuThread(); Device.Gpu.SetGpuThread();
@ -241,6 +257,14 @@ namespace Ryujinx.Headless.SDL2
_exitEvent.Dispose(); _exitEvent.Dispose();
} }
public void ProcessMainThreadQueue()
{
while (MainThreadActions.TryDequeue(out Action action))
{
action();
}
}
public void MainLoop() public void MainLoop()
{ {
while (_isActive) while (_isActive)
@ -249,6 +273,8 @@ namespace Ryujinx.Headless.SDL2
SDL_PumpEvents(); SDL_PumpEvents();
ProcessMainThreadQueue();
// Polling becomes expensive if it's not slept // Polling becomes expensive if it's not slept
Thread.Sleep(1); Thread.Sleep(1);
} }
@ -315,14 +341,6 @@ namespace Ryujinx.Headless.SDL2
InitializeWindow(); InitializeWindow();
InitializeWindowRenderer();
Device.Gpu.Renderer.Initialize(_glLogLevel);
InitializeRenderer();
_gpuVendorName = GetGpuVendorName();
Thread renderLoopThread = new Thread(Render) Thread renderLoopThread = new Thread(Render)
{ {
Name = "GUI.RenderLoop" Name = "GUI.RenderLoop"

View file

@ -153,7 +153,8 @@ namespace Ryujinx.Memory
if (OperatingSystem.IsMacOSVersionAtLeast(10, 14)) if (OperatingSystem.IsMacOSVersionAtLeast(10, 14))
{ {
result |= MAP_JIT_DARWIN; // Only to be used with the Hardened Runtime.
// result |= MAP_JIT_DARWIN;
} }
return result; return result;

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Modules
public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { } public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { }
private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetObject("UpdateDialog").Handle) private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetRawOwnedObject("UpdateDialog"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);

View file

@ -16,6 +16,7 @@ using Ryujinx.Ui.Widgets;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -40,6 +41,12 @@ namespace Ryujinx
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type); public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
[DllImport("libc", SetLastError = true)]
static extern int setenv(string name, string value, int overwrite);
[DllImport("libc")]
static extern IntPtr getenv(string name);
private const uint MB_ICONWARNING = 0x30; private const uint MB_ICONWARNING = 0x30;
static Program() static Program()
@ -97,6 +104,35 @@ namespace Ryujinx
XInitThreads(); XInitThreads();
} }
if (OperatingSystem.IsMacOS())
{
string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
string resourcesDataDir;
if (Path.GetFileName(baseDirectory) == "MacOS")
{
resourcesDataDir = Path.Combine(Directory.GetParent(baseDirectory).FullName, "Resources");
}
else
{
resourcesDataDir = baseDirectory;
}
void SetEnvironmentVariableNoCaching(string key, string value)
{
int res = setenv(key, value, 1);
Debug.Assert(res != -1);
}
// On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
// On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
}
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");

View file

@ -19,10 +19,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="GtkSharp" Version="3.22.25.128" /> <PackageReference Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="GtkSharp.Dependencies" Version="1.1.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="GtkSharp.Dependencies.osx" Version="0.0.5" Condition="'$(RuntimeIdentifier)' == 'osx-x64' OR '$(RuntimeIdentifier)' == 'osx-arm64'" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.osx" Version="5.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="OpenTK.Core" Version="4.7.5" /> <PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="OpenTK.Graphics" Version="4.7.5" /> <PackageReference Include="OpenTK.Graphics" Version="4.7.5" />
<PackageReference Include="SPB" Version="0.0.4-build28" /> <PackageReference Include="SPB" Version="0.0.4-build28" />
@ -62,10 +65,6 @@
<ApplicationIcon>Ryujinx.ico</ApplicationIcon> <ApplicationIcon>Ryujinx.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'osx-x64'">
<DefineConstants>$(DefineConstants);MACOS_BUILD</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Ui\MainWindow.glade" /> <None Remove="Ui\MainWindow.glade" />
<None Remove="Ui\Widgets\ProfileDialog.glade" /> <None Remove="Ui\Widgets\ProfileDialog.glade" />

View file

@ -0,0 +1,134 @@
using Gdk;
using System;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
namespace Ryujinx.Ui.Helper
{
public delegate void UpdateBoundsCallbackDelegate(Window window);
[SupportedOSPlatform("macos")]
static class MetalHelper
{
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
private struct Selector
{
public readonly IntPtr NativePtr;
public unsafe Selector(string value)
{
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
byte* data = stackalloc byte[size];
fixed (char* pValue = value)
{
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
}
NativePtr = sel_registerName(data);
}
public static implicit operator Selector(string value) => new Selector(value);
}
private static unsafe IntPtr GetClass(string value)
{
int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length);
byte* data = stackalloc byte[size];
fixed (char* pValue = value)
{
System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size);
}
return objc_getClass(data);
}
private struct NSPoint
{
public double X;
public double Y;
public NSPoint(double x, double y)
{
X = x;
Y = y;
}
}
private struct NSRect
{
public NSPoint Pos;
public NSPoint Size;
public NSRect(double x, double y, double width, double height)
{
Pos = new NSPoint(x, y);
Size = new NSPoint(width, height);
}
}
public static IntPtr GetMetalLayer(Display display, Window window, out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds)
{
nsView = gdk_quartz_window_get_nsview(window.Handle);
// Create a new CAMetalLayer.
IntPtr layerClass = GetClass("CAMetalLayer");
IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc");
objc_msgSend(metalLayer, "init");
// Create a child NSView to render into.
IntPtr nsViewClass = GetClass("NSView");
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
objc_msgSend(child, "init", new NSRect());
// Add it as a child.
objc_msgSend(nsView, "addSubview:", child);
// Make its renderer our metal layer.
objc_msgSend(child, "setWantsLayer:", (byte)1);
objc_msgSend(child, "setLayer:", metalLayer);
objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor);
// Set the frame position/location.
updateBounds = (Window window) => {
window.GetPosition(out int x, out int y);
int width = window.Width;
int height = window.Height;
objc_msgSend(child, "setFrame:", new NSRect(x, y, width, height));
};
updateBounds(window);
return metalLayer;
}
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr sel_registerName(byte* data);
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr objc_getClass(byte* data);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, double value);
[DllImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
[DllImport("libgdk-3.0.dylib")]
private static extern IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow);
}
}

View file

@ -142,7 +142,7 @@ namespace Ryujinx.Ui
public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { } public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { }
private MainWindow(Builder builder) : base(builder.GetObject("_mainWin").Handle) private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("_mainWin"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
@ -846,9 +846,7 @@ namespace Ryujinx.Ui
_deviceExitStatus.Reset(); _deviceExitStatus.Reset();
Translator.IsReadyForTranslation.Reset(); Translator.IsReadyForTranslation.Reset();
#if MACOS_BUILD
CreateGameWindow();
#else
Thread windowThread = new Thread(() => Thread windowThread = new Thread(() =>
{ {
CreateGameWindow(); CreateGameWindow();
@ -858,7 +856,6 @@ namespace Ryujinx.Ui
}; };
windowThread.Start(); windowThread.Start();
#endif
_gameLoaded = true; _gameLoaded = true;
_actionMenu.Sensitive = true; _actionMenu.Sensitive = true;

View file

@ -1,9 +1,11 @@
using Gdk; using Gdk;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Ui.Helper;
using SPB.Graphics.Vulkan; using SPB.Graphics.Vulkan;
using SPB.Platform.Win32; using SPB.Platform.Win32;
using SPB.Platform.X11; using SPB.Platform.X11;
using SPB.Platform.Metal;
using SPB.Windowing; using SPB.Windowing;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -13,6 +15,7 @@ namespace Ryujinx.Ui
public class VKRenderer : RendererWidgetBase public class VKRenderer : RendererWidgetBase
{ {
public NativeWindowBase NativeWindow { get; private set; } public NativeWindowBase NativeWindow { get; private set; }
private UpdateBoundsCallbackDelegate _updateBoundsCallback;
public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { }
@ -31,6 +34,12 @@ namespace Ryujinx.Ui
return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle));
} }
else if (OperatingSystem.IsMacOS())
{
IntPtr metalLayer = MetalHelper.GetMetalLayer(Display, Window, out IntPtr nsView, out _updateBoundsCallback);
return new SimpleMetalWindow(new NativeHandle(nsView), new NativeHandle(metalLayer));
}
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -53,7 +62,11 @@ namespace Ryujinx.Ui
WaitEvent.Set(); WaitEvent.Set();
} }
return base.OnConfigureEvent(evnt); bool result = base.OnConfigureEvent(evnt);
_updateBoundsCallback?.Invoke(Window);
return result;
} }
public unsafe IntPtr CreateWindowSurface(IntPtr instance) public unsafe IntPtr CreateWindowSurface(IntPtr instance)

View file

@ -18,7 +18,7 @@ namespace Ryujinx.Ui.Widgets
public ProfileDialog() : this(new Builder("Ryujinx.Ui.Widgets.ProfileDialog.glade")) { } public ProfileDialog() : this(new Builder("Ryujinx.Ui.Widgets.ProfileDialog.glade")) { }
private ProfileDialog(Builder builder) : base(builder.GetObject("_profileDialog").Handle) private ProfileDialog(Builder builder) : base(builder.GetRawOwnedObject("_profileDialog"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");

View file

@ -23,7 +23,7 @@ namespace Ryujinx.Ui.Windows
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { } public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { }
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetObject("_cheatWindow").Handle) private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetRawOwnedObject("_cheatWindow"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";

View file

@ -119,7 +119,7 @@ namespace Ryujinx.Ui.Windows
public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { } public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { }
private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetObject("_controllerWin").Handle) private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin"))
{ {
_mainWindow = mainWindow; _mainWindow = mainWindow;
_selectedGamepad = null; _selectedGamepad = null;
@ -379,6 +379,8 @@ namespace Ryujinx.Ui.Windows
break; break;
} }
if (!OperatingSystem.IsMacOS())
{
_controllerImage.Pixbuf = _controllerType.ActiveId switch _controllerImage.Pixbuf = _controllerType.ActiveId switch
{ {
"ProController" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_ProCon.svg", 400, 400), "ProController" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_ProCon.svg", 400, 400),
@ -387,6 +389,7 @@ namespace Ryujinx.Ui.Windows
_ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConPair.svg", 400, 500), _ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Controller_JoyConPair.svg", 400, 500),
}; };
} }
}
private void ClearValues() private void ClearValues()
{ {

View file

@ -34,7 +34,7 @@ namespace Ryujinx.Ui.Windows
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { } public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_dlcWindow").Handle) private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_dlcWindow"))
{ {
builder.Autoconnect(this); builder.Autoconnect(this);

View file

@ -113,7 +113,7 @@ namespace Ryujinx.Ui.Windows
public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(parent, new Builder("Ryujinx.Ui.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { } public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(parent, new Builder("Ryujinx.Ui.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle) private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : base(builder.GetRawOwnedObject("_settingsWin"))
{ {
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
@ -422,7 +422,7 @@ namespace Ryujinx.Ui.Windows
Task.Run(() => Task.Run(() =>
{ {
openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported;
soundIoIsSupported = SoundIoHardwareDeviceDriver.IsSupported; soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported;
sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported;
}); });
@ -438,6 +438,15 @@ namespace Ryujinx.Ui.Windows
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
}); });
if (OperatingSystem.IsMacOS())
{
var store = (_graphicsBackend.Model as ListStore);
store.GetIter(out TreeIter openglIter, new TreePath(new int[] {1}));
store.Remove(ref openglIter);
_graphicsBackend.Model = store;
}
} }
private void UpdatePreferredGpuComboBox() private void UpdatePreferredGpuComboBox()

View file

@ -40,7 +40,7 @@ namespace Ryujinx.Ui.Windows
public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { } public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { }
private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_titleUpdateWindow").Handle) private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_titleUpdateWindow"))
{ {
_parent = parent; _parent = parent;