aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Ava/AppHost.cs92
-rw-r--r--Ryujinx.Ava/Input/AvaloniaMouseDriver.cs15
-rw-r--r--Ryujinx.Ava/Program.cs50
-rw-r--r--Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs69
-rw-r--r--Ryujinx.Ava/Ui/Backend/BackendSurface.cs76
-rw-r--r--Ryujinx.Ava/Ui/Backend/Interop.cs49
-rw-r--r--Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs26
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs17
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs201
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs61
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs53
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs13
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs134
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs215
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs63
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs456
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs165
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs135
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs59
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs44
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs219
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs70
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs18
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs32
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs75
-rw-r--r--Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs43
-rw-r--r--Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs78
-rw-r--r--Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs204
-rw-r--r--Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs85
-rw-r--r--Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs192
-rw-r--r--Ryujinx.Ava/Ui/Controls/RendererControl.cs96
-rw-r--r--Ryujinx.Ava/Ui/Controls/RendererHost.axaml14
-rw-r--r--Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs126
-rw-r--r--Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs33
-rw-r--r--Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs220
-rw-r--r--Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs113
-rw-r--r--Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs64
-rw-r--r--Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml27
-rw-r--r--Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs25
-rw-r--r--Ryujinx.Ava/Ui/Windows/MainWindow.axaml1
-rw-r--r--Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs25
-rw-r--r--Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs12
-rw-r--r--Ryujinx.Graphics.GAL/IWindow.cs2
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs4
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs4
-rw-r--r--Ryujinx.Graphics.Gpu/Window.cs2
-rw-r--r--Ryujinx.Graphics.OpenGL/Window.cs61
-rw-r--r--Ryujinx.Graphics.Vulkan/ImageWindow.cs429
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs66
-rw-r--r--Ryujinx.Graphics.Vulkan/Window.cs2
-rw-r--r--Ryujinx.Graphics.Vulkan/WindowBase.cs2
-rw-r--r--Ryujinx.HLE/Switch.cs2
-rw-r--r--Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs24
-rw-r--r--Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs2
-rw-r--r--Ryujinx.Headless.SDL2/WindowBase.cs4
-rw-r--r--Ryujinx/Ui/GLRenderer.cs24
-rw-r--r--Ryujinx/Ui/RendererWidgetBase.cs4
-rw-r--r--Ryujinx/Ui/VKRenderer.cs2
58 files changed, 868 insertions, 3531 deletions
diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs
index 7e3cddc8..7cf5934a 100644
--- a/Ryujinx.Ava/AppHost.cs
+++ b/Ryujinx.Ava/AppHost.cs
@@ -1,6 +1,5 @@
using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
-using Avalonia;
using Avalonia.Input;
using Avalonia.Threading;
using LibHac.Tools.FsSystem;
@@ -12,10 +11,8 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
-using Ryujinx.Ava.Ui.Backend.Vulkan;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
-using Ryujinx.Ava.Ui.Vulkan;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
@@ -39,6 +36,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
+using SPB.Graphics.Vulkan;
using System;
using System.Diagnostics;
using System.IO;
@@ -58,24 +56,24 @@ namespace Ryujinx.Ava
{
private const int CursorHideIdleTime = 8; // Hide Cursor seconds
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
+ private const int TargetFps = 60;
- private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None);
+ private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None);
+ private readonly long _ticksPerFrame;
+ private readonly Stopwatch _chrono;
private readonly AccountManager _accountManager;
private readonly UserChannelPersistence _userChannelPersistence;
-
private readonly InputManager _inputManager;
-
- private readonly IKeyboard _keyboardInterface;
-
private readonly MainWindow _parent;
-
+ private readonly IKeyboard _keyboardInterface;
private readonly GraphicsDebugLevel _glLogLevel;
private bool _hideCursorOnIdle;
private bool _isStopped;
private bool _isActive;
private long _lastCursorMoveTime;
+ private long _ticks = 0;
private KeyboardHotkeyState _prevHotkeyState;
@@ -93,7 +91,7 @@ namespace Ryujinx.Ava
public event EventHandler AppExit;
public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
- public RendererControl Renderer { get; }
+ public RendererHost Renderer { get; }
public VirtualFileSystem VirtualFileSystem { get; }
public ContentManager ContentManager { get; }
public Switch Device { get; set; }
@@ -111,7 +109,7 @@ namespace Ryujinx.Ava
private object _lockObject = new();
public AppHost(
- RendererControl renderer,
+ RendererHost renderer,
InputManager inputManager,
string applicationPath,
VirtualFileSystem virtualFileSystem,
@@ -128,7 +126,7 @@ namespace Ryujinx.Ava
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
_lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
- _inputManager.SetMouseDriver(new AvaloniaMouseDriver(renderer));
+ _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_parent, renderer));
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
NpadManager = _inputManager.CreateNpadManager();
@@ -138,6 +136,9 @@ namespace Ryujinx.Ava
VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager;
+ _chrono = new Stopwatch();
+ _ticksPerFrame = Stopwatch.Frequency / TargetFps;
+
if (ApplicationPath.StartsWith("@SystemContent"))
{
ApplicationPath = _parent.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
@@ -177,7 +178,7 @@ namespace Ryujinx.Ava
if (_renderer != null)
{
double scale = _parent.PlatformImpl.RenderScaling;
- _renderer.Window.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
+ _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
}
}
@@ -335,8 +336,6 @@ namespace Ryujinx.Ava
return;
}
- AvaloniaLocator.Current.GetService<VulkanPlatformInterface>()?.MainSurface.Display.ChangeVSyncMode(true);
-
_isStopped = true;
_isActive = false;
}
@@ -376,6 +375,8 @@ namespace Ryujinx.Ava
_gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose();
+
+ _chrono.Stop();
}
public void DisposeGpu()
@@ -389,8 +390,7 @@ namespace Ryujinx.Ava
Renderer?.MakeCurrent();
Device.DisposeGpu();
-
- Renderer?.DestroyBackgroundContext();
+
Renderer?.MakeCurrent(null);
}
@@ -596,16 +596,11 @@ namespace Ryujinx.Ava
IRenderer renderer;
- if (Program.UseVulkan)
+ if (Renderer.IsVulkan)
{
- var vulkan = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
+ string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
- renderer = new VulkanRenderer(vulkan.Instance.InternalHandle,
- vulkan.MainSurface.Device.InternalHandle,
- vulkan.PhysicalDevice.InternalHandle,
- vulkan.MainSurface.Device.Queue.InternalHandle,
- vulkan.PhysicalDevice.QueueFamilyIndex,
- vulkan.MainSurface.Device.Lock);
+ renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
}
else
{
@@ -778,11 +773,7 @@ namespace Ryujinx.Ava
{
Width = (int)e.Width;
Height = (int)e.Height;
-
- if (!Program.UseVulkan)
- {
- SetRendererWindowSize(e);
- }
+ SetRendererWindowSize(e);
}
private void MainLoop()
@@ -822,12 +813,10 @@ namespace Ryujinx.Ava
_renderer.ScreenCaptured += Renderer_ScreenCaptured;
- (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext((Renderer as OpenGLRendererControl).GameContext));
+ (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Renderer.GetContext()));
Renderer.MakeCurrent();
- AvaloniaLocator.Current.GetService<VulkanPlatformInterface>()?.MainSurface?.Display?.ChangeVSyncMode(Device.EnableDeviceVsync);
-
Device.Gpu.Renderer.Initialize(_glLogLevel);
Width = (int)Renderer.Bounds.Width;
@@ -835,16 +824,20 @@ namespace Ryujinx.Ava
_renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling));
+ _chrono.Start();
+
Device.Gpu.Renderer.RunLoop(() =>
{
Device.Gpu.SetGpuThread();
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
Translator.IsReadyForTranslation.Set();
- Renderer.Start();
-
while (_isActive)
{
+ _ticks += _chrono.ElapsedTicks;
+
+ _chrono.Restart();
+
if (Device.WaitFifo())
{
Device.Statistics.RecordFifoStart();
@@ -860,19 +853,20 @@ namespace Ryujinx.Ava
_parent.SwitchToGameControl();
}
- Device.PresentFrame(Present);
+ Device.PresentFrame(() => Renderer?.SwapBuffers());
}
- }
- Renderer.Stop();
+ if (_ticks >= _ticksPerFrame)
+ {
+ UpdateStatus();
+ }
+ }
});
Renderer?.MakeCurrent(null);
-
- Renderer.SizeChanged -= Window_SizeChanged;
}
- private void Present(object image)
+ public void UpdateStatus()
{
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"];
@@ -886,24 +880,12 @@ namespace Ryujinx.Ava
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
Device.EnableDeviceVsync,
Device.GetVolume(),
- Program.UseVulkan ? "Vulkan" : "OpenGL",
+ Renderer.IsVulkan ? "Vulkan" : "OpenGL",
dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
-
- if (Program.UseVulkan)
- {
- var platformInterface = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
- if (platformInterface.MainSurface.Display.IsSurfaceChanged())
- {
- SetRendererWindowSize(new Size(Width, Height));
- return;
- }
- }
-
- Renderer.Present(image);
}
public async Task ShowExitPrompt()
@@ -985,8 +967,6 @@ namespace Ryujinx.Ava
case KeyboardHotkeyState.ToggleVSync:
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
- AvaloniaLocator.Current.GetService<VulkanPlatformInterface>()?.MainSurface?.Display?.ChangeVSyncMode(Device.EnableDeviceVsync);
-
break;
case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true;
diff --git a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs
index 74c435b5..9ad0310a 100644
--- a/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs
+++ b/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs
@@ -14,20 +14,27 @@ namespace Ryujinx.Ava.Input
private Control _widget;
private bool _isDisposed;
private Size _size;
+ private readonly Window _window;
public bool[] PressedButtons { get; }
public Vector2 CurrentPosition { get; private set; }
public Vector2 Scroll { get; private set; }
- public AvaloniaMouseDriver(Control parent)
+ public AvaloniaMouseDriver(Window window, Control parent)
{
_widget = parent;
+ _window = window;
_widget.PointerMoved += Parent_PointerMovedEvent;
_widget.PointerPressed += Parent_PointerPressEvent;
_widget.PointerReleased += Parent_PointerReleaseEvent;
_widget.PointerWheelChanged += Parent_ScrollEvent;
+
+ _window.PointerMoved += Parent_PointerMovedEvent;
+ _window.PointerPressed += Parent_PointerPressEvent;
+ _window.PointerReleased += Parent_PointerReleaseEvent;
+ _window.PointerWheelChanged += Parent_ScrollEvent;
PressedButtons = new bool[(int)MouseButton.Count];
@@ -47,7 +54,6 @@ namespace Ryujinx.Ava.Input
private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args)
{
- var pointerProperties = args.GetCurrentPoint(_widget).Properties;
PressedButtons[(int)args.InitialPressMouseButton - 1] = false;
}
@@ -125,6 +131,11 @@ namespace Ryujinx.Ava.Input
_widget.PointerReleased -= Parent_PointerReleaseEvent;
_widget.PointerWheelChanged -= Parent_ScrollEvent;
+ _window.PointerMoved -= Parent_PointerMovedEvent;
+ _window.PointerPressed -= Parent_PointerPressEvent;
+ _window.PointerReleased -= Parent_PointerReleaseEvent;
+ _window.PointerWheelChanged -= Parent_ScrollEvent;
+
_widget = null;
}
}
diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs
index 242246eb..61b184c6 100644
--- a/Ryujinx.Ava/Program.cs
+++ b/Ryujinx.Ava/Program.cs
@@ -1,9 +1,7 @@
using ARMeilleure.Translation.PTC;
using Avalonia;
-using Avalonia.OpenGL;
using Avalonia.Rendering;
using Avalonia.Threading;
-using Ryujinx.Ava.Ui.Backend;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
@@ -12,12 +10,10 @@ using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
using Ryujinx.Common.SystemInfo;
-using Ryujinx.Graphics.Vulkan;
using Ryujinx.Modules;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using System;
-using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
@@ -34,7 +30,6 @@ namespace Ryujinx.Ava
public static bool PreviewerDetached { get; private set; }
public static RenderTimer RenderTimer { get; private set; }
- public static bool UseVulkan { get; private set; }
[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
@@ -71,36 +66,16 @@ namespace Ryujinx.Ava
EnableMultiTouch = true,
EnableIme = true,
UseEGL = false,
- UseGpu = !UseVulkan,
- GlProfiles = new List<GlVersion>()
- {
- new GlVersion(GlProfileType.OpenGL, 4, 3)
- }
+ UseGpu = false
})
.With(new Win32PlatformOptions
{
EnableMultitouch = true,
- UseWgl = !UseVulkan,
- WglProfiles = new List<GlVersion>()
- {
- new GlVersion(GlProfileType.OpenGL, 4, 3)
- },
+ UseWgl = false,
AllowEglInitialization = false,
CompositionBackdropCornerRadius = 8f,
})
.UseSkia()
- .With(new Ui.Vulkan.VulkanOptions()
- {
- ApplicationName = "Ryujinx.Graphics.Vulkan",
- MaxQueueCount = 2,
- PreferDiscreteGpu = true,
- PreferredDevice = !PreviewerDetached ? "" : ConfigurationState.Instance.Graphics.PreferredGpu.Value,
- UseDebug = !PreviewerDetached ? false : ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value != GraphicsDebugLevel.None,
- })
- .With(new SkiaOptions()
- {
- CustomGpuFactory = UseVulkan ? SkiaGpuFactory.CreateVulkanGpu : null
- })
.AfterSetup(_ =>
{
AvaloniaLocator.CurrentMutable
@@ -176,26 +151,7 @@ namespace Ryujinx.Ava
ReloadConfig();
- UseVulkan = PreviewerDetached ? ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan : false;
-
- if (UseVulkan)
- {
- if (VulkanRenderer.GetPhysicalDevices().Length == 0)
- {
- UseVulkan = false;
-
- ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl;
-
- Logger.Warning?.PrintMsg(LogClass.Application, "A suitable Vulkan physical device is not available. Falling back to OpenGL");
- }
- }
-
- if (UseVulkan)
- {
- // With a custom gpu backend, avalonia doesn't enable dpi awareness, so the backend must handle it. This isn't so for the opengl backed,
- // as that uses avalonia's gpu backend and it's enabled there.
- ForceDpiAware.Windows();
- }
+ ForceDpiAware.Windows();
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
ActualScaleFactor = ForceDpiAware.GetActualScaleFactor() / BaseDpi;
diff --git a/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs b/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs
index 564ee4b2..e4ddba96 100644
--- a/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs
+++ b/Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs
@@ -1,3 +1,4 @@
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
@@ -59,29 +60,63 @@ namespace Ryujinx.Ava.Ui.Controls
string input = string.Empty;
+ var overlay = new ContentDialogOverlayWindow()
+ {
+ Height = window.Bounds.Height,
+ Width = window.Bounds.Width,
+ Position = window.PointToScreen(new Point())
+ };
+
+ window.PositionChanged += OverlayOnPositionChanged;
+
+ void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
+ {
+ overlay.Position = window.PointToScreen(new Point());
+ }
+
+ contentDialog = overlay.ContentDialog;
+
+ bool opened = false;
+
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
- if (contentDialog != null)
+ content._host = contentDialog;
+ contentDialog.Title = title;
+ contentDialog.PrimaryButtonText = args.SubmitText;
+ contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
+ contentDialog.SecondaryButtonText = "";
+ contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"];
+ contentDialog.Content = content;
+
+ TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
{
- content._host = contentDialog;
- contentDialog.Title = title;
- contentDialog.PrimaryButtonText = args.SubmitText;
- contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
- contentDialog.SecondaryButtonText = "";
- contentDialog.CloseButtonText = LocaleManager.Instance["InputDialogCancel"];
- contentDialog.Content = content;
- TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) =>
+ if (eventArgs.Result == ContentDialogResult.Primary)
{
- if (eventArgs.Result == ContentDialogResult.Primary)
- {
- result = UserResult.Ok;
- input = content.Input.Text;
- }
- };
- contentDialog.Closed += handler;
+ result = UserResult.Ok;
+ input = content.Input.Text;
+ }
+ };
+ contentDialog.Closed += handler;
+
+ overlay.Opened += OverlayOnActivated;
+
+ async void OverlayOnActivated(object sender, EventArgs e)
+ {
+ if (opened)
+ {
+ return;
+ }
+
+ opened = true;
+
+ overlay.Position = window.PointToScreen(new Point());
+
await contentDialog.ShowAsync();
contentDialog.Closed -= handler;
- }
+ overlay.Close();
+ };
+
+ await overlay.ShowDialog(window);
return (result, input);
}
diff --git a/Ryujinx.Ava/Ui/Backend/BackendSurface.cs b/Ryujinx.Ava/Ui/Backend/BackendSurface.cs
deleted file mode 100644
index 423fe038..00000000
--- a/Ryujinx.Ava/Ui/Backend/BackendSurface.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Avalonia;
-using System;
-using System.Runtime.InteropServices;
-using static Ryujinx.Ava.Ui.Backend.Interop;
-
-namespace Ryujinx.Ava.Ui.Backend
-{
- public abstract class BackendSurface : IDisposable
- {
- protected IntPtr Display => _display;
-
- private IntPtr _display = IntPtr.Zero;
-
- [DllImport("libX11.so.6")]
- public static extern IntPtr XOpenDisplay(IntPtr display);
-
- [DllImport("libX11.so.6")]
- public static extern int XCloseDisplay(IntPtr display);
-
- private PixelSize _currentSize;
- public IntPtr Handle { get; protected set; }
-
- public bool IsDisposed { get; private set; }
-
- public BackendSurface(IntPtr handle)
- {
- Handle = handle;
-
- if (OperatingSystem.IsLinux())
- {
- _display = XOpenDisplay(IntPtr.Zero);
- }
- }
-
- public PixelSize Size
- {
- get
- {
- PixelSize size = new PixelSize();
- if (OperatingSystem.IsWindows())
- {
- GetClientRect(Handle, out var rect);
- size = new PixelSize(rect.right, rect.bottom);
- }
- else if (OperatingSystem.IsLinux())
- {
- XWindowAttributes attributes = new XWindowAttributes();
- XGetWindowAttributes(Display, Handle, ref attributes);
-
- size = new PixelSize(attributes.width, attributes.height);
- }
-
- _currentSize = size;
-
- return size;
- }
- }
-
- public PixelSize CurrentSize => _currentSize;
-
- public virtual void Dispose()
- {
- if (IsDisposed)
- {
- throw new ObjectDisposedException(nameof(BackendSurface));
- }
-
- IsDisposed = true;
-
- if (_display != IntPtr.Zero)
- {
- XCloseDisplay(_display);
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Backend/Interop.cs b/Ryujinx.Ava/Ui/Backend/Interop.cs
deleted file mode 100644
index 617e9767..00000000
--- a/Ryujinx.Ava/Ui/Backend/Interop.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using FluentAvalonia.Interop;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Ava.Ui.Backend
-{
- public static class Interop
- {
- [StructLayout(LayoutKind.Sequential)]
- public struct XWindowAttributes
- {
- public int x;
- public int y;
- public int width;
- public int height;
- public int border_width;
- public int depth;
- public IntPtr visual;
- public IntPtr root;
- public int c_class;
- public int bit_gravity;
- public int win_gravity;
- public int backing_store;
- public IntPtr backing_planes;
- public IntPtr backing_pixel;
- public int save_under;
- public IntPtr colormap;
- public int map_installed;
- public int map_state;
- public IntPtr all_event_masks;
- public IntPtr your_event_mask;
- public IntPtr do_not_propagate_mask;
- public int override_direct;
- public IntPtr screen;
- }
-
- [DllImport("user32.dll")]
- public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
-
- [DllImport("libX11.so.6")]
- public static extern int XCloseDisplay(IntPtr display);
-
- [DllImport("libX11.so.6")]
- public static extern int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes);
-
- [DllImport("libX11.so.6")]
- public static extern IntPtr XOpenDisplay(IntPtr display);
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs b/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs
deleted file mode 100644
index 335bc905..00000000
--- a/Ryujinx.Ava/Ui/Backend/SkiaGpuFactory.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Avalonia;
-using Avalonia.Skia;
-using Ryujinx.Ava.Ui.Vulkan;
-using Ryujinx.Ava.Ui.Backend.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Backend
-{
- public static class SkiaGpuFactory
- {
- public static ISkiaGpu CreateVulkanGpu()
- {
- var skiaOptions = AvaloniaLocator.Current.GetService<SkiaOptions>() ?? new SkiaOptions();
- var platformInterface = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
-
- if (platformInterface == null)
- {
- VulkanPlatformInterface.TryInitialize();
- }
-
- var gpu = new VulkanSkiaGpu(skiaOptions.MaxGpuResourceSizeBytes);
- AvaloniaLocator.CurrentMutable.Bind<VulkanSkiaGpu>().ToConstant(gpu);
-
- return gpu;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs
deleted file mode 100644
index 2a1cd229..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/ResultExtensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- public static class ResultExtensions
- {
- public static void ThrowOnError(this Result result)
- {
- // Only negative result codes are errors.
- if ((int)result < (int)Result.Success)
- {
- throw new Exception($"Unexpected API error \"{result}\".");
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs
deleted file mode 100644
index 70ec39c7..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using System;
-using Avalonia;
-using Avalonia.Skia;
-using Ryujinx.Ava.Ui.Vulkan;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Silk.NET.Vulkan;
-using SkiaSharp;
-
-namespace Ryujinx.Ava.Ui.Backend.Vulkan
-{
- internal class VulkanRenderTarget : ISkiaGpuRenderTarget
- {
- public GRContext GrContext { get; private set; }
-
- private readonly VulkanSurfaceRenderTarget _surface;
- private readonly VulkanPlatformInterface _vulkanPlatformInterface;
- private readonly IVulkanPlatformSurface _vulkanPlatformSurface;
- private GRVkBackendContext _grVkBackend;
-
- public VulkanRenderTarget(VulkanPlatformInterface vulkanPlatformInterface, IVulkanPlatformSurface vulkanPlatformSurface)
- {
- _surface = vulkanPlatformInterface.CreateRenderTarget(vulkanPlatformSurface);
- _vulkanPlatformInterface = vulkanPlatformInterface;
- _vulkanPlatformSurface = vulkanPlatformSurface;
-
- Initialize();
- }
-
- private void Initialize()
- {
- GRVkGetProcedureAddressDelegate getProc = GetVulkanProcAddress;
-
- _grVkBackend = new GRVkBackendContext()
- {
- VkInstance = _surface.Device.Handle,
- VkPhysicalDevice = _vulkanPlatformInterface.PhysicalDevice.Handle,
- VkDevice = _surface.Device.Handle,
- VkQueue = _surface.Device.Queue.Handle,
- GraphicsQueueIndex = _vulkanPlatformInterface.PhysicalDevice.QueueFamilyIndex,
- GetProcedureAddress = getProc
- };
-
- GrContext = GRContext.CreateVulkan(_grVkBackend);
-
- var gpu = AvaloniaLocator.Current.GetService<VulkanSkiaGpu>();
-
- if (gpu.MaxResourceBytes.HasValue)
- {
- GrContext.SetResourceCacheLimit(gpu.MaxResourceBytes.Value);
- }
- }
-
- private IntPtr GetVulkanProcAddress(string name, IntPtr instanceHandle, IntPtr deviceHandle)
- {
- IntPtr addr;
-
- if (deviceHandle != IntPtr.Zero)
- {
- addr = _vulkanPlatformInterface.Api.GetDeviceProcAddr(new Device(deviceHandle), name);
-
- if (addr != IntPtr.Zero)
- {
- return addr;
- }
-
- addr = _vulkanPlatformInterface.Api.GetDeviceProcAddr(new Device(_surface.Device.Handle), name);
-
- if (addr != IntPtr.Zero)
- {
- return addr;
- }
- }
-
- addr = _vulkanPlatformInterface.Api.GetInstanceProcAddr(new Instance(_vulkanPlatformInterface.Instance.Handle), name);
-
- if (addr == IntPtr.Zero)
- {
- addr = _vulkanPlatformInterface.Api.GetInstanceProcAddr(new Instance(instanceHandle), name);
- }
-
- return addr;
- }
-
- public void Dispose()
- {
- _grVkBackend.Dispose();
- GrContext.Dispose();
- _surface.Dispose();
- }
-
- public ISkiaGpuRenderSession BeginRenderingSession()
- {
- var session = _surface.BeginDraw(_vulkanPlatformSurface.Scaling);
- bool success = false;
- try
- {
- var disp = session.Display;
- var api = session.Api;
-
- var size = session.Size;
- var scaling = session.Scaling;
- if (size.Width <= 0 || size.Height <= 0 || scaling < 0)
- {
- size = new Avalonia.PixelSize(1, 1);
- scaling = 1;
- }
-
- lock (GrContext)
- {
- GrContext.ResetContext();
-
- var image = _surface.GetImage();
-
- var imageInfo = new GRVkImageInfo()
- {
- CurrentQueueFamily = disp.QueueFamilyIndex,
- Format = (uint)image.Format,
- Image = image.Handle,
- ImageLayout = (uint)image.CurrentLayout,
- ImageTiling = (uint)image.Tiling,
- ImageUsageFlags = _surface.UsageFlags,
- LevelCount = _surface.MipLevels,
- SampleCount = 1,
- Protected = false,
- Alloc = new GRVkAlloc()
- {
- Memory = image.MemoryHandle,
- Flags = 0,
- Offset = 0,
- Size = _surface.MemorySize
- }
- };
-
- var renderTarget =
- new GRBackendRenderTarget((int)size.Width, (int)size.Height, 1,
- imageInfo);
- var surface = SKSurface.Create(GrContext, renderTarget,
- GRSurfaceOrigin.TopLeft,
- _surface.IsRgba ? SKColorType.Rgba8888 : SKColorType.Bgra8888, SKColorSpace.CreateSrgb());
-
- if (surface == null)
- {
- throw new InvalidOperationException(
- "Surface can't be created with the provided render target");
- }
-
- success = true;
-
- return new VulkanGpuSession(GrContext, renderTarget, surface, session);
- }
- }
- finally
- {
- if (!success)
- {
- session.Dispose();
- }
- }
- }
-
- public bool IsCorrupted { get; }
-
- internal class VulkanGpuSession : ISkiaGpuRenderSession
- {
- private readonly GRBackendRenderTarget _backendRenderTarget;
- private readonly VulkanSurfaceRenderingSession _vulkanSession;
-
- public VulkanGpuSession(GRContext grContext,
- GRBackendRenderTarget backendRenderTarget,
- SKSurface surface,
- VulkanSurfaceRenderingSession vulkanSession)
- {
- GrContext = grContext;
- _backendRenderTarget = backendRenderTarget;
- SkSurface = surface;
- _vulkanSession = vulkanSession;
-
- SurfaceOrigin = GRSurfaceOrigin.TopLeft;
- }
-
- public void Dispose()
- {
- lock (_vulkanSession.Display.Lock)
- {
- SkSurface.Canvas.Flush();
-
- SkSurface.Dispose();
- _backendRenderTarget.Dispose();
- GrContext.Flush();
-
- _vulkanSession.Dispose();
- }
- }
-
- public GRContext GrContext { get; }
- public SKSurface SkSurface { get; }
- public double ScaleFactor => _vulkanSession.Scaling;
- public GRSurfaceOrigin SurfaceOrigin { get; }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs
deleted file mode 100644
index a5c27086..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSkiaGpu.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia;
-using Avalonia.Platform;
-using Avalonia.Skia;
-using Avalonia.X11;
-using Ryujinx.Ava.Ui.Vulkan;
-using Silk.NET.Vulkan;
-using SkiaSharp;
-
-namespace Ryujinx.Ava.Ui.Backend.Vulkan
-{
- public class VulkanSkiaGpu : ISkiaGpu
- {
- private readonly VulkanPlatformInterface _vulkan;
- public long? MaxResourceBytes { get; }
-
- public VulkanSkiaGpu(long? maxResourceBytes)
- {
- _vulkan = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
- MaxResourceBytes = maxResourceBytes;
- }
-
- public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces)
- {
- foreach (var surface in surfaces)
- {
- VulkanWindowSurface window;
-
- if (surface is IPlatformHandle handle)
- {
- window = new VulkanWindowSurface(handle.Handle);
- }
- else if (surface is X11FramebufferSurface x11FramebufferSurface)
- {
- // As of Avalonia 0.10.13, an IPlatformHandle isn't passed for linux, so use reflection to otherwise get the window id
- var xId = (IntPtr)x11FramebufferSurface.GetType().GetField(
- "_xid",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(x11FramebufferSurface);
-
- window = new VulkanWindowSurface(xId);
- }
- else
- {
- continue;
- }
-
- VulkanRenderTarget vulkanRenderTarget = new VulkanRenderTarget(_vulkan, window);
-
- return vulkanRenderTarget;
- }
-
- return null;
- }
-
- public ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session)
- {
- return null;
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs
deleted file mode 100644
index fd2d379b..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanSurface.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using Avalonia;
-using Ryujinx.Ava.Ui.Vulkan;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Silk.NET.Vulkan;
-using Silk.NET.Vulkan.Extensions.KHR;
-using System;
-
-namespace Ryujinx.Ava.Ui.Backend.Vulkan
-{
- internal class VulkanWindowSurface : BackendSurface, IVulkanPlatformSurface
- {
- public float Scaling => (float)Program.ActualScaleFactor;
-
- public PixelSize SurfaceSize => Size;
-
- public VulkanWindowSurface(IntPtr handle) : base(handle)
- {
- }
-
- public unsafe SurfaceKHR CreateSurface(VulkanInstance instance)
- {
- if (OperatingSystem.IsWindows())
- {
- if (instance.Api.TryGetInstanceExtension(new Instance(instance.Handle), out KhrWin32Surface surfaceExtension))
- {
- var createInfo = new Win32SurfaceCreateInfoKHR() { Hinstance = 0, Hwnd = Handle, SType = StructureType.Win32SurfaceCreateInfoKhr };
-
- surfaceExtension.CreateWin32Surface(new Instance(instance.Handle), createInfo, null, out var surface).ThrowOnError();
-
- return surface;
- }
- }
- else if (OperatingSystem.IsLinux())
- {
- if (instance.Api.TryGetInstanceExtension(new Instance(instance.Handle), out KhrXlibSurface surfaceExtension))
- {
- var createInfo = new XlibSurfaceCreateInfoKHR()
- {
- SType = StructureType.XlibSurfaceCreateInfoKhr,
- Dpy = (nint*)Display,
- Window = Handle
- };
-
- surfaceExtension.CreateXlibSurface(new Instance(instance.Handle), createInfo, null, out var surface).ThrowOnError();
-
- return surface;
- }
- }
-
- throw new PlatformNotSupportedException("The current platform does not support surface creation.");
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs
deleted file mode 100644
index 642d8a6a..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/IVulkanPlatformSurface.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using Avalonia;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan.Surfaces
-{
- public interface IVulkanPlatformSurface : IDisposable
- {
- float Scaling { get; }
- PixelSize SurfaceSize { get; }
- SurfaceKHR CreateSurface(VulkanInstance instance);
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs
deleted file mode 100644
index 510e6724..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/Surfaces/VulkanSurfaceRenderTarget.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using System;
-using Avalonia;
-using Ryujinx.Graphics.Vulkan;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan.Surfaces
-{
- internal class VulkanSurfaceRenderTarget : IDisposable
- {
- private readonly VulkanPlatformInterface _platformInterface;
- private readonly Format _format;
-
- private VulkanCommandBufferPool.VulkanCommandBuffer _commandBuffer;
- private VulkanImage Image { get; set; }
- private object _lock = new object();
-
- public uint MipLevels => Image.MipLevels;
- public VulkanDevice Device { get; }
-
- public VulkanSurfaceRenderTarget(VulkanPlatformInterface platformInterface, VulkanSurface surface)
- {
- _platformInterface = platformInterface;
-
- var device = VulkanInitialization.CreateDevice(platformInterface.Api,
- platformInterface.PhysicalDevice.InternalHandle,
- platformInterface.PhysicalDevice.QueueFamilyIndex,
- VulkanInitialization.GetSupportedExtensions(platformInterface.Api, platformInterface.PhysicalDevice.InternalHandle),
- platformInterface.PhysicalDevice.QueueCount);
-
- Device = new VulkanDevice(device, platformInterface.PhysicalDevice, platformInterface.Api);
-
- Display = VulkanDisplay.CreateDisplay(
- platformInterface.Instance,
- Device,
- platformInterface.PhysicalDevice,
- surface);
- Surface = surface;
-
- // Skia seems to only create surfaces from images with unorm format
- IsRgba = Display.SurfaceFormat.Format >= Format.R8G8B8A8Unorm &&
- Display.SurfaceFormat.Format <= Format.R8G8B8A8Srgb;
-
- _format = IsRgba ? Format.R8G8B8A8Unorm : Format.B8G8R8A8Unorm;
- }
-
- public bool IsRgba { get; }
-
- public uint ImageFormat => (uint)_format;
-
- public ulong MemorySize => Image.MemorySize;
-
- public VulkanDisplay Display { get; private set; }
-
- public VulkanSurface Surface { get; private set; }
-
- public uint UsageFlags => Image.UsageFlags;
-
- public PixelSize Size { get; private set; }
-
- public void Dispose()
- {
- lock (_lock)
- {
- DestroyImage();
- Display?.Dispose();
- Surface?.Dispose();
- Device?.Dispose();
-
- Display = null;
- Surface = null;
- }
- }
-
- public VulkanSurfaceRenderingSession BeginDraw(float scaling)
- {
- if (Image == null)
- {
- RecreateImage();
- }
-
- _commandBuffer?.WaitForFence();
- _commandBuffer = null;
-
- var session = new VulkanSurfaceRenderingSession(Display, Device, this, scaling);
-
- Image.TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr);
-
- return session;
- }
-
- public void RecreateImage()
- {
- DestroyImage();
- CreateImage();
- }
-
- private void CreateImage()
- {
- Size = Display.Size;
-
- Image = new VulkanImage(Device, _platformInterface.PhysicalDevice, Display.CommandBufferPool, ImageFormat, Size);
- }
-
- private void DestroyImage()
- {
- _commandBuffer?.WaitForFence();
- _commandBuffer = null;
- Image?.Dispose();
- Image = null;
- }
-
- public VulkanImage GetImage()
- {
- return Image;
- }
-
- public void EndDraw()
- {
- lock (_lock)
- {
- if (Display == null)
- {
- return;
- }
-
- _commandBuffer = Display.StartPresentation();
-
- Display.BlitImageToCurrentImage(this, _commandBuffer.InternalHandle);
-
- Display.EndPresentation(_commandBuffer);
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs
deleted file mode 100644
index a00ecf2b..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanCommandBufferPool.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanCommandBufferPool : IDisposable
- {
- private readonly VulkanDevice _device;
- private readonly CommandPool _commandPool;
-
- private readonly List<VulkanCommandBuffer> _usedCommandBuffers = new();
- private readonly object _lock = new object();
-
- public unsafe VulkanCommandBufferPool(VulkanDevice device, VulkanPhysicalDevice physicalDevice)
- {
- _device = device;
-
- var commandPoolCreateInfo = new CommandPoolCreateInfo
- {
- SType = StructureType.CommandPoolCreateInfo,
- Flags = CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit,
- QueueFamilyIndex = physicalDevice.QueueFamilyIndex
- };
-
- device.Api.CreateCommandPool(_device.InternalHandle, commandPoolCreateInfo, null, out _commandPool)
- .ThrowOnError();
- }
-
- private CommandBuffer AllocateCommandBuffer()
- {
- var commandBufferAllocateInfo = new CommandBufferAllocateInfo
- {
- SType = StructureType.CommandBufferAllocateInfo,
- CommandPool = _commandPool,
- CommandBufferCount = 1,
- Level = CommandBufferLevel.Primary
- };
-
- lock (_lock)
- {
- _device.Api.AllocateCommandBuffers(_device.InternalHandle, commandBufferAllocateInfo, out var commandBuffer);
-
- return commandBuffer;
- }
- }
-
- public VulkanCommandBuffer CreateCommandBuffer()
- {
- return new(_device, this);
- }
-
- public void FreeUsedCommandBuffers()
- {
- lock (_lock)
- {
- foreach (var usedCommandBuffer in _usedCommandBuffers)
- {
- usedCommandBuffer.Dispose();
- }
-
- _usedCommandBuffers.Clear();
- }
- }
-
- private void DisposeCommandBuffer(VulkanCommandBuffer commandBuffer)
- {
- lock (_lock)
- {
- _usedCommandBuffers.Add(commandBuffer);
- }
- }
-
- public void Dispose()
- {
- lock (_lock)
- {
- FreeUsedCommandBuffers();
- _device.Api.DestroyCommandPool(_device.InternalHandle, _commandPool, Span<AllocationCallbacks>.Empty);
- }
- }
-
- public class VulkanCommandBuffer : IDisposable
- {
- private readonly VulkanCommandBufferPool _commandBufferPool;
- private readonly VulkanDevice _device;
- private readonly Fence _fence;
- private bool _hasEnded;
- private bool _hasStarted;
- private bool _isDisposed;
- private object _lock = new object();
-
- public IntPtr Handle => InternalHandle.Handle;
-
- internal CommandBuffer InternalHandle { get; }
-
- internal unsafe VulkanCommandBuffer(VulkanDevice device, VulkanCommandBufferPool commandBufferPool)
- {
- _device = device;
- _commandBufferPool = commandBufferPool;
-
- InternalHandle = _commandBufferPool.AllocateCommandBuffer();
-
- var fenceCreateInfo = new FenceCreateInfo()
- {
- SType = StructureType.FenceCreateInfo,
- Flags = FenceCreateFlags.FenceCreateSignaledBit
- };
-
- device.Api.CreateFence(device.InternalHandle, fenceCreateInfo, null, out _fence);
- }
-
- public void WaitForFence()
- {
- if (_isDisposed)
- {
- return;
- }
-
- lock (_lock)
- {
- if (!_isDisposed)
- {
- _device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue);
- }
- }
- }
-
- public void BeginRecording()
- {
- if (!_hasStarted)
- {
- _hasStarted = true;
-
- var beginInfo = new CommandBufferBeginInfo
- {
- SType = StructureType.CommandBufferBeginInfo,
- Flags = CommandBufferUsageFlags.CommandBufferUsageOneTimeSubmitBit
- };
-
- _device.Api.BeginCommandBuffer(InternalHandle, beginInfo);
- }
- }
-
- public void EndRecording()
- {
- if (_hasStarted && !_hasEnded)
- {
- _hasEnded = true;
-
- _device.Api.EndCommandBuffer(InternalHandle);
- }
- }
-
- public void Submit()
- {
- Submit(null, null, null, _fence);
- }
-
- public unsafe void Submit(
- ReadOnlySpan<Semaphore> waitSemaphores,
- ReadOnlySpan<PipelineStageFlags> waitDstStageMask,
- ReadOnlySpan<Semaphore> signalSemaphores,
- Fence? fence = null)
- {
- EndRecording();
-
- if (!fence.HasValue)
- {
- fence = _fence;
- }
-
- fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
- {
- fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask)
- {
- var commandBuffer = InternalHandle;
- var submitInfo = new SubmitInfo
- {
- SType = StructureType.SubmitInfo,
- WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
- PWaitSemaphores = pWaitSemaphores,
- PWaitDstStageMask = pWaitDstStageMask,
- CommandBufferCount = 1,
- PCommandBuffers = &commandBuffer,
- SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0,
- PSignalSemaphores = pSignalSemaphores,
- };
-
- _device.Api.ResetFences(_device.InternalHandle, 1, fence.Value);
-
- _device.Submit(submitInfo, fence.Value);
- }
- }
-
- _commandBufferPool.DisposeCommandBuffer(this);
- }
-
- public void Dispose()
- {
- lock (_lock)
- {
- if (!_isDisposed)
- {
- _isDisposed = true;
-
- _device.Api.WaitForFences(_device.InternalHandle, 1, _fence, true, ulong.MaxValue);
- _device.Api.FreeCommandBuffers(_device.InternalHandle, _commandBufferPool._commandPool, 1, InternalHandle);
- _device.Api.DestroyFence(_device.InternalHandle, _fence, Span<AllocationCallbacks>.Empty);
- }
- }
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs
deleted file mode 100644
index 3d893e19..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDevice.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanDevice : IDisposable
- {
- private static object _lock = new object();
-
- public VulkanDevice(Device apiHandle, VulkanPhysicalDevice physicalDevice, Vk api)
- {
- InternalHandle = apiHandle;
- Api = api;
-
- api.GetDeviceQueue(apiHandle, physicalDevice.QueueFamilyIndex, 0, out var queue);
-
- Queue = new VulkanQueue(this, queue);
-
- PresentQueue = Queue;
- }
-
- public IntPtr Handle => InternalHandle.Handle;
-
- internal Device InternalHandle { get; }
- public Vk Api { get; }
-
- public VulkanQueue Queue { get; private set; }
- public VulkanQueue PresentQueue { get; }
-
- public void Dispose()
- {
- WaitIdle();
- Queue = null;
- Api.DestroyDevice(InternalHandle, Span<AllocationCallbacks>.Empty);
- }
-
- internal void Submit(SubmitInfo submitInfo, Fence fence = default)
- {
- lock (_lock)
- {
- Api.QueueSubmit(Queue.InternalHandle, 1, submitInfo, fence).ThrowOnError();
- }
- }
-
- public void WaitIdle()
- {
- lock (_lock)
- {
- Api.DeviceWaitIdle(InternalHandle);
- }
- }
-
- public void QueueWaitIdle()
- {
- lock (_lock)
- {
- Api.QueueWaitIdle(Queue.InternalHandle);
- }
- }
-
- public object Lock => _lock;
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs
deleted file mode 100644
index f3116fbd..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanDisplay.cs
+++ /dev/null
@@ -1,456 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using Avalonia;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Silk.NET.Vulkan;
-using Silk.NET.Vulkan.Extensions.KHR;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanDisplay : IDisposable
- {
- private static KhrSwapchain _swapchainExtension;
- private readonly VulkanInstance _instance;
- private readonly VulkanPhysicalDevice _physicalDevice;
- private readonly VulkanSemaphorePair _semaphorePair;
- private readonly VulkanDevice _device;
- private uint _nextImage;
- private readonly VulkanSurface _surface;
- private SurfaceFormatKHR _surfaceFormat;
- private SwapchainKHR _swapchain;
- private Extent2D _swapchainExtent;
- private Image[] _swapchainImages;
- private ImageView[] _swapchainImageViews = Array.Empty<ImageView>();
- private bool _vsyncStateChanged;
- private bool _vsyncEnabled;
- private bool _surfaceChanged;
-
- public event EventHandler Presented;
-
- public VulkanCommandBufferPool CommandBufferPool { get; set; }
-
- public object Lock => _device.Lock;
-
- private VulkanDisplay(VulkanInstance instance, VulkanDevice device,
- VulkanPhysicalDevice physicalDevice, VulkanSurface surface, SwapchainKHR swapchain,
- Extent2D swapchainExtent)
- {
- _instance = instance;
- _device = device;
- _physicalDevice = physicalDevice;
- _swapchain = swapchain;
- _swapchainExtent = swapchainExtent;
- _surface = surface;
-
- CreateSwapchainImages();
-
- _semaphorePair = new VulkanSemaphorePair(_device);
-
- CommandBufferPool = new VulkanCommandBufferPool(device, physicalDevice);
- }
-
- public PixelSize Size { get; private set; }
- public uint QueueFamilyIndex => _physicalDevice.QueueFamilyIndex;
-
- internal SurfaceFormatKHR SurfaceFormat
- {
- get
- {
- if (_surfaceFormat.Format == Format.Undefined)
- {
- _surfaceFormat = _surface.GetSurfaceFormat(_physicalDevice);
- }
-
- return _surfaceFormat;
- }
- }
-
- public void Dispose()
- {
- _device.WaitIdle();
- _semaphorePair?.Dispose();
- DestroyCurrentImageViews();
- _swapchainExtension.DestroySwapchain(_device.InternalHandle, _swapchain, Span<AllocationCallbacks>.Empty);
- CommandBufferPool.Dispose();
- }
-
- public bool IsSurfaceChanged()
- {
- var changed = _surfaceChanged;
- _surfaceChanged = false;
-
- return changed;
- }
-
- private static unsafe SwapchainKHR CreateSwapchain(VulkanInstance instance, VulkanDevice device,
- VulkanPhysicalDevice physicalDevice, VulkanSurface surface, out Extent2D swapchainExtent,
- SwapchainKHR? oldswapchain = null, bool vsyncEnabled = true)
- {
- if (_swapchainExtension == null)
- {
- instance.Api.TryGetDeviceExtension(instance.InternalHandle, device.InternalHandle, out _swapchainExtension);
- }
-
- while (!surface.CanSurfacePresent(physicalDevice))
- {
- Thread.Sleep(16);
- }
-
- VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfaceCapabilities(physicalDevice.InternalHandle,
- surface.ApiHandle, out var capabilities);
-
- var imageCount = capabilities.MinImageCount + 1;
- if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount)
- {
- imageCount = capabilities.MaxImageCount;
- }
-
- var surfaceFormat = surface.GetSurfaceFormat(physicalDevice);
-
- bool supportsIdentityTransform = capabilities.SupportedTransforms.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr);
- bool isRotated = capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate90BitKhr) ||
- capabilities.CurrentTransform.HasFlag(SurfaceTransformFlagsKHR.SurfaceTransformRotate270BitKhr);
-
- swapchainExtent = GetSwapchainExtent(surface, capabilities);
-
- CompositeAlphaFlagsKHR compositeAlphaFlags = GetSuitableCompositeAlphaFlags(capabilities);
-
- PresentModeKHR presentMode = GetSuitablePresentMode(physicalDevice, surface, vsyncEnabled);
-
- var swapchainCreateInfo = new SwapchainCreateInfoKHR
- {
- SType = StructureType.SwapchainCreateInfoKhr,
- Surface = surface.ApiHandle,
- MinImageCount = imageCount,
- ImageFormat = surfaceFormat.Format,
- ImageColorSpace = surfaceFormat.ColorSpace,
- ImageExtent = swapchainExtent,
- ImageUsage =
- ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit,
- ImageSharingMode = SharingMode.Exclusive,
- ImageArrayLayers = 1,
- PreTransform = supportsIdentityTransform && isRotated ?
- SurfaceTransformFlagsKHR.SurfaceTransformIdentityBitKhr :
- capabilities.CurrentTransform,
- CompositeAlpha = compositeAlphaFlags,
- PresentMode = presentMode,
- Clipped = true,
- OldSwapchain = oldswapchain ?? new SwapchainKHR()
- };
-
- _swapchainExtension.CreateSwapchain(device.InternalHandle, swapchainCreateInfo, null, out var swapchain)
- .ThrowOnError();
-
- if (oldswapchain != null)
- {
- _swapchainExtension.DestroySwapchain(device.InternalHandle, oldswapchain.Value, null);
- }
-
- return swapchain;
- }
-
- private static unsafe Extent2D GetSwapchainExtent(VulkanSurface surface, SurfaceCapabilitiesKHR capabilities)
- {
- Extent2D swapchainExtent;
- if (capabilities.CurrentExtent.Width != uint.MaxValue)
- {
- swapchainExtent = capabilities.CurrentExtent;
- }
- else
- {
- var surfaceSize = surface.SurfaceSize;
-
- var width = Math.Clamp((uint)surfaceSize.Width, capabilities.MinImageExtent.Width, capabilities.MaxImageExtent.Width);
- var height = Math.Clamp((uint)surfaceSize.Height, capabilities.MinImageExtent.Height, capabilities.MaxImageExtent.Height);
-
- swapchainExtent = new Extent2D(width, height);
- }
-
- return swapchainExtent;
- }
-
- private static unsafe CompositeAlphaFlagsKHR GetSuitableCompositeAlphaFlags(SurfaceCapabilitiesKHR capabilities)
- {
- var compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr;
-
- if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr))
- {
- compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPostMultipliedBitKhr;
- }
- else if (capabilities.SupportedCompositeAlpha.HasFlag(CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr))
- {
- compositeAlphaFlags = CompositeAlphaFlagsKHR.CompositeAlphaPreMultipliedBitKhr;
- }
-
- return compositeAlphaFlags;
- }
-
- private static unsafe PresentModeKHR GetSuitablePresentMode(VulkanPhysicalDevice physicalDevice, VulkanSurface surface, bool vsyncEnabled)
- {
- uint presentModesCount;
-
- VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle,
- surface.ApiHandle,
- &presentModesCount, null);
-
- var presentModes = new PresentModeKHR[presentModesCount];
-
- fixed (PresentModeKHR* pPresentModes = presentModes)
- {
- VulkanSurface.SurfaceExtension.GetPhysicalDeviceSurfacePresentModes(physicalDevice.InternalHandle,
- surface.ApiHandle, &presentModesCount, pPresentModes);
- }
-
- var modes = presentModes.ToList();
-
- if (!vsyncEnabled && modes.Contains(PresentModeKHR.PresentModeImmediateKhr))
- {
- return PresentModeKHR.PresentModeImmediateKhr;
- }
- else if (modes.Contains(PresentModeKHR.PresentModeMailboxKhr))
- {
- return PresentModeKHR.PresentModeMailboxKhr;
- }
- else if (modes.Contains(PresentModeKHR.PresentModeFifoKhr))
- {
- return PresentModeKHR.PresentModeFifoKhr;
- }
- else
- {
- return PresentModeKHR.PresentModeImmediateKhr;
- }
- }
-
- internal static VulkanDisplay CreateDisplay(VulkanInstance instance, VulkanDevice device,
- VulkanPhysicalDevice physicalDevice, VulkanSurface surface)
- {
- var swapchain = CreateSwapchain(instance, device, physicalDevice, surface, out var extent, null, true);
-
- return new VulkanDisplay(instance, device, physicalDevice, surface, swapchain, extent);
- }
-
- private unsafe void CreateSwapchainImages()
- {
- DestroyCurrentImageViews();
-
- Size = new PixelSize((int)_swapchainExtent.Width, (int)_swapchainExtent.Height);
-
- uint imageCount = 0;
-
- _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, null);
-
- _swapchainImages = new Image[imageCount];
-
- fixed (Image* pSwapchainImages = _swapchainImages)
- {
- _swapchainExtension.GetSwapchainImages(_device.InternalHandle, _swapchain, &imageCount, pSwapchainImages);
- }
-
- _swapchainImageViews = new ImageView[imageCount];
-
- var surfaceFormat = SurfaceFormat;
-
- for (var i = 0; i < imageCount; i++)
- {
- _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
- }
- }
-
- private void DestroyCurrentImageViews()
- {
- for (var i = 0; i < _swapchainImageViews.Length; i++)
- {
- _instance.Api.DestroyImageView(_device.InternalHandle, _swapchainImageViews[i], Span<AllocationCallbacks>.Empty);
- }
- }
-
- internal void ChangeVSyncMode(bool vsyncEnabled)
- {
- _vsyncStateChanged = true;
- _vsyncEnabled = vsyncEnabled;
- }
-
- private void Recreate()
- {
- _device.WaitIdle();
- _swapchain = CreateSwapchain(_instance, _device, _physicalDevice, _surface, out _swapchainExtent, _swapchain, _vsyncEnabled);
-
- CreateSwapchainImages();
-
- _surfaceChanged = true;
- }
-
- private unsafe ImageView CreateSwapchainImageView(Image swapchainImage, Format format)
- {
- var componentMapping = new ComponentMapping(
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity);
-
- var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
-
- var imageCreateInfo = new ImageViewCreateInfo
- {
- SType = StructureType.ImageViewCreateInfo,
- Image = swapchainImage,
- ViewType = ImageViewType.ImageViewType2D,
- Format = format,
- Components = componentMapping,
- SubresourceRange = subresourceRange
- };
-
- _instance.Api.CreateImageView(_device.InternalHandle, imageCreateInfo, null, out var imageView).ThrowOnError();
- return imageView;
- }
-
- public bool EnsureSwapchainAvailable()
- {
- if (Size != _surface.SurfaceSize || _vsyncStateChanged)
- {
- _vsyncStateChanged = false;
-
- Recreate();
-
- return false;
- }
-
- return true;
- }
-
- internal VulkanCommandBufferPool.VulkanCommandBuffer StartPresentation()
- {
- _nextImage = 0;
- while (true)
- {
- var acquireResult = _swapchainExtension.AcquireNextImage(
- _device.InternalHandle,
- _swapchain,
- ulong.MaxValue,
- _semaphorePair.ImageAvailableSemaphore,
- new Fence(),
- ref _nextImage);
-
- if (acquireResult == Result.ErrorOutOfDateKhr ||
- acquireResult == Result.SuboptimalKhr)
- {
- Recreate();
- }
- else
- {
- acquireResult.ThrowOnError();
- break;
- }
- }
-
- var commandBuffer = CommandBufferPool.CreateCommandBuffer();
- commandBuffer.BeginRecording();
-
- VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle,
- _swapchainImages[_nextImage], ImageLayout.Undefined,
- AccessFlags.AccessNoneKhr,
- ImageLayout.TransferDstOptimal,
- AccessFlags.AccessTransferWriteBit,
- 1);
-
- return commandBuffer;
- }
-
- internal void BlitImageToCurrentImage(VulkanSurfaceRenderTarget renderTarget, CommandBuffer commandBuffer)
- {
- var image = renderTarget.GetImage();
-
- VulkanMemoryHelper.TransitionLayout(_device, commandBuffer,
- image.InternalHandle.Value, (ImageLayout)image.CurrentLayout,
- AccessFlags.AccessNoneKhr,
- ImageLayout.TransferSrcOptimal,
- AccessFlags.AccessTransferReadBit,
- renderTarget.MipLevels);
-
- var srcBlitRegion = new ImageBlit
- {
- SrcOffsets = new ImageBlit.SrcOffsetsBuffer
- {
- Element0 = new Offset3D(0, 0, 0),
- Element1 = new Offset3D(renderTarget.Size.Width, renderTarget.Size.Height, 1),
- },
- DstOffsets = new ImageBlit.DstOffsetsBuffer
- {
- Element0 = new Offset3D(0, 0, 0),
- Element1 = new Offset3D(Size.Width, Size.Height, 1),
- },
- SrcSubresource = new ImageSubresourceLayers
- {
- AspectMask = ImageAspectFlags.ImageAspectColorBit,
- BaseArrayLayer = 0,
- LayerCount = 1,
- MipLevel = 0
- },
- DstSubresource = new ImageSubresourceLayers
- {
- AspectMask = ImageAspectFlags.ImageAspectColorBit,
- BaseArrayLayer = 0,
- LayerCount = 1,
- MipLevel = 0
- }
- };
-
- _device.Api.CmdBlitImage(commandBuffer, image.InternalHandle.Value,
- ImageLayout.TransferSrcOptimal,
- _swapchainImages[_nextImage],
- ImageLayout.TransferDstOptimal,
- 1,
- srcBlitRegion,
- Filter.Linear);
-
- VulkanMemoryHelper.TransitionLayout(_device, commandBuffer,
- image.InternalHandle.Value, ImageLayout.TransferSrcOptimal,
- AccessFlags.AccessTransferReadBit,
- (ImageLayout)image.CurrentLayout,
- AccessFlags.AccessNoneKhr,
- renderTarget.MipLevels);
- }
-
- internal unsafe void EndPresentation(VulkanCommandBufferPool.VulkanCommandBuffer commandBuffer)
- {
- VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle,
- _swapchainImages[_nextImage], ImageLayout.TransferDstOptimal,
- AccessFlags.AccessNoneKhr,
- ImageLayout.PresentSrcKhr,
- AccessFlags.AccessNoneKhr,
- 1);
-
- commandBuffer.Submit(
- stackalloc[] { _semaphorePair.ImageAvailableSemaphore },
- stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit },
- stackalloc[] { _semaphorePair.RenderFinishedSemaphore });
-
- var semaphore = _semaphorePair.RenderFinishedSemaphore;
- var swapchain = _swapchain;
- var nextImage = _nextImage;
-
- Result result;
-
- var presentInfo = new PresentInfoKHR
- {
- SType = StructureType.PresentInfoKhr,
- WaitSemaphoreCount = 1,
- PWaitSemaphores = &semaphore,
- SwapchainCount = 1,
- PSwapchains = &swapchain,
- PImageIndices = &nextImage,
- PResults = &result
- };
-
- lock (_device.Lock)
- {
- _swapchainExtension.QueuePresent(_device.PresentQueue.InternalHandle, presentInfo);
- }
-
- CommandBufferPool.FreeUsedCommandBuffers();
-
- Presented?.Invoke(this, null);
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs
deleted file mode 100644
index 3fbb8665..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanImage.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using System;
-using Avalonia;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanImage : IDisposable
- {
- private readonly VulkanDevice _device;
- private readonly VulkanPhysicalDevice _physicalDevice;
- private readonly VulkanCommandBufferPool _commandBufferPool;
- private ImageLayout _currentLayout;
- private AccessFlags _currentAccessFlags;
- private ImageUsageFlags _imageUsageFlags { get; }
- private ImageView? _imageView { get; set; }
- private DeviceMemory _imageMemory { get; set; }
-
- internal Image? InternalHandle { get; private set; }
- internal Format Format { get; }
- internal ImageAspectFlags AspectFlags { get; private set; }
-
- public ulong Handle => InternalHandle?.Handle ?? 0;
- public ulong ViewHandle => _imageView?.Handle ?? 0;
- public uint UsageFlags => (uint)_imageUsageFlags;
- public ulong MemoryHandle => _imageMemory.Handle;
- public uint MipLevels { get; private set; }
- public PixelSize Size { get; }
- public ulong MemorySize { get; private set; }
- public uint CurrentLayout => (uint)_currentLayout;
-
- public VulkanImage(
- VulkanDevice device,
- VulkanPhysicalDevice physicalDevice,
- VulkanCommandBufferPool commandBufferPool,
- uint format,
- PixelSize size,
- uint mipLevels = 0)
- {
- _device = device;
- _physicalDevice = physicalDevice;
- _commandBufferPool = commandBufferPool;
- Format = (Format)format;
- Size = size;
- MipLevels = mipLevels;
- _imageUsageFlags =
- ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferDstBit |
- ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageSampledBit;
-
- Initialize();
- }
-
- public unsafe void Initialize()
- {
- if (!InternalHandle.HasValue)
- {
- MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
-
- var imageCreateInfo = new ImageCreateInfo
- {
- SType = StructureType.ImageCreateInfo,
- ImageType = ImageType.ImageType2D,
- Format = Format,
- Extent = new Extent3D((uint?)Size.Width, (uint?)Size.Height, 1),
- MipLevels = MipLevels,
- ArrayLayers = 1,
- Samples = SampleCountFlags.SampleCount1Bit,
- Tiling = Tiling,
- Usage = _imageUsageFlags,
- SharingMode = SharingMode.Exclusive,
- InitialLayout = ImageLayout.Undefined,
- Flags = ImageCreateFlags.ImageCreateMutableFormatBit
- };
-
- _device.Api.CreateImage(_device.InternalHandle, imageCreateInfo, null, out var image).ThrowOnError();
- InternalHandle = image;
-
- _device.Api.GetImageMemoryRequirements(_device.InternalHandle, InternalHandle.Value,
- out var memoryRequirements);
-
- var memoryAllocateInfo = new MemoryAllocateInfo
- {
- SType = StructureType.MemoryAllocateInfo,
- AllocationSize = memoryRequirements.Size,
- MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
- _physicalDevice,
- memoryRequirements.MemoryTypeBits, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit)
- };
-
- _device.Api.AllocateMemory(_device.InternalHandle, memoryAllocateInfo, null,
- out var imageMemory);
-
- _imageMemory = imageMemory;
-
- _device.Api.BindImageMemory(_device.InternalHandle, InternalHandle.Value, _imageMemory, 0);
-
- MemorySize = memoryRequirements.Size;
-
- var componentMapping = new ComponentMapping(
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity,
- ComponentSwizzle.Identity);
-
- AspectFlags = ImageAspectFlags.ImageAspectColorBit;
-
- var subresourceRange = new ImageSubresourceRange(AspectFlags, 0, MipLevels, 0, 1);
-
- var imageViewCreateInfo = new ImageViewCreateInfo
- {
- SType = StructureType.ImageViewCreateInfo,
- Image = InternalHandle.Value,
- ViewType = ImageViewType.ImageViewType2D,
- Format = Format,
- Components = componentMapping,
- SubresourceRange = subresourceRange
- };
-
- _device.Api
- .CreateImageView(_device.InternalHandle, imageViewCreateInfo, null, out var imageView)
- .ThrowOnError();
-
- _imageView = imageView;
-
- _currentLayout = ImageLayout.Undefined;
-
- TransitionLayout(ImageLayout.ColorAttachmentOptimal, AccessFlags.AccessNoneKhr);
- }
- }
-
- public ImageTiling Tiling => ImageTiling.Optimal;
-
- internal void TransitionLayout(ImageLayout destinationLayout, AccessFlags destinationAccessFlags)
- {
- var commandBuffer = _commandBufferPool.CreateCommandBuffer();
- commandBuffer.BeginRecording();
-
- VulkanMemoryHelper.TransitionLayout(_device, commandBuffer.InternalHandle, InternalHandle.Value,
- _currentLayout,
- _currentAccessFlags,
- destinationLayout, destinationAccessFlags,
- MipLevels);
-
- commandBuffer.EndRecording();
-
- commandBuffer.Submit();
-
- _currentLayout = destinationLayout;
- _currentAccessFlags = destinationAccessFlags;
- }
-
- public void Dispose()
- {
- if (InternalHandle != null)
- {
- _device.Api.DestroyImageView(_device.InternalHandle, _imageView.Value, Span<AllocationCallbacks>.Empty);
- _device.Api.DestroyImage(_device.InternalHandle, InternalHandle.Value, Span<AllocationCallbacks>.Empty);
- _device.Api.FreeMemory(_device.InternalHandle, _imageMemory, Span<AllocationCallbacks>.Empty);
-
- _imageView = default;
- InternalHandle = null;
- _imageMemory = default;
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs
deleted file mode 100644
index b50e9c07..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanInstance.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using Silk.NET.Core;
-using Silk.NET.Vulkan;
-using Silk.NET.Vulkan.Extensions.EXT;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- public class VulkanInstance : IDisposable
- {
- private const string EngineName = "Avalonia Vulkan";
-
- private VulkanInstance(Instance apiHandle, Vk api)
- {
- InternalHandle = apiHandle;
- Api = api;
- }
-
- public IntPtr Handle => InternalHandle.Handle;
-
- internal Instance InternalHandle { get; }
- public Vk Api { get; }
-
- internal static IEnumerable<string> RequiredInstanceExtensions
- {
- get
- {
- yield return "VK_KHR_surface";
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- yield return "VK_KHR_xlib_surface";
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- yield return "VK_KHR_win32_surface";
- }
- }
- }
-
- public void Dispose()
- {
- Api?.DestroyInstance(InternalHandle, Span<AllocationCallbacks>.Empty);
- Api?.Dispose();
- }
-
- internal static unsafe VulkanInstance Create(VulkanOptions options)
- {
- var api = Vk.GetApi();
- var applicationName = Marshal.StringToHGlobalAnsi(options.ApplicationName);
- var engineName = Marshal.StringToHGlobalAnsi(EngineName);
- var enabledExtensions = new List<string>(options.InstanceExtensions);
-
- enabledExtensions.AddRange(RequiredInstanceExtensions);
-
- var applicationInfo = new ApplicationInfo
- {
- PApplicationName = (byte*)applicationName,
- ApiVersion = Vk.Version12.Value,
- PEngineName = (byte*)engineName,
- EngineVersion = new Version32(1, 0, 0),
- ApplicationVersion = new Version32(1, 0, 0)
- };
-
- var enabledLayers = new HashSet<string>();
-
- if (options.UseDebug)
- {
- enabledExtensions.Add(ExtDebugUtils.ExtensionName);
- enabledExtensions.Add(ExtDebugReport.ExtensionName);
- if (IsLayerAvailable(api, "VK_LAYER_KHRONOS_validation"))
- enabledLayers.Add("VK_LAYER_KHRONOS_validation");
- }
-
- foreach (var layer in options.EnabledLayers)
- enabledLayers.Add(layer);
-
- var ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Count];
- var ppEnabledLayers = stackalloc IntPtr[enabledLayers.Count];
-
- for (var i = 0; i < enabledExtensions.Count; i++)
- ppEnabledExtensions[i] = Marshal.StringToHGlobalAnsi(enabledExtensions[i]);
-
- var layers = enabledLayers.ToList();
-
- for (var i = 0; i < enabledLayers.Count; i++)
- ppEnabledLayers[i] = Marshal.StringToHGlobalAnsi(layers[i]);
-
- var instanceCreateInfo = new InstanceCreateInfo
- {
- SType = StructureType.InstanceCreateInfo,
- PApplicationInfo = &applicationInfo,
- PpEnabledExtensionNames = (byte**)ppEnabledExtensions,
- PpEnabledLayerNames = (byte**)ppEnabledLayers,
- EnabledExtensionCount = (uint)enabledExtensions.Count,
- EnabledLayerCount = (uint)enabledLayers.Count
- };
-
- api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
-
- Marshal.FreeHGlobal(applicationName);
- Marshal.FreeHGlobal(engineName);
-
- for (var i = 0; i < enabledExtensions.Count; i++) Marshal.FreeHGlobal(ppEnabledExtensions[i]);
-
- for (var i = 0; i < enabledLayers.Count; i++) Marshal.FreeHGlobal(ppEnabledLayers[i]);
-
- return new VulkanInstance(instance, api);
- }
-
- private static unsafe bool IsLayerAvailable(Vk api, string layerName)
- {
- uint layerPropertiesCount;
-
- api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
-
- var layerProperties = new LayerProperties[layerPropertiesCount];
-
- fixed (LayerProperties* pLayerProperties = layerProperties)
- {
- api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError();
-
- for (var i = 0; i < layerPropertiesCount; i++)
- {
- var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName);
-
- if (currentLayerName == layerName) return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs
deleted file mode 100644
index a7052592..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanMemoryHelper.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal static class VulkanMemoryHelper
- {
- internal static int FindSuitableMemoryTypeIndex(VulkanPhysicalDevice physicalDevice, uint memoryTypeBits,
- MemoryPropertyFlags flags)
- {
- physicalDevice.Api.GetPhysicalDeviceMemoryProperties(physicalDevice.InternalHandle, out var properties);
-
- for (var i = 0; i < properties.MemoryTypeCount; i++)
- {
- var type = properties.MemoryTypes[i];
-
- if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) return i;
- }
-
- return -1;
- }
-
- internal static unsafe void TransitionLayout(VulkanDevice device,
- CommandBuffer commandBuffer,
- Image image,
- ImageLayout sourceLayout,
- AccessFlags sourceAccessMask,
- ImageLayout destinationLayout,
- AccessFlags destinationAccessMask,
- uint mipLevels)
- {
- var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, mipLevels, 0, 1);
-
- var barrier = new ImageMemoryBarrier
- {
- SType = StructureType.ImageMemoryBarrier,
- SrcAccessMask = sourceAccessMask,
- DstAccessMask = destinationAccessMask,
- OldLayout = sourceLayout,
- NewLayout = destinationLayout,
- SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
- DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
- Image = image,
- SubresourceRange = subresourceRange
- };
-
- device.Api.CmdPipelineBarrier(
- commandBuffer,
- PipelineStageFlags.PipelineStageAllCommandsBit,
- PipelineStageFlags.PipelineStageAllCommandsBit,
- 0,
- 0,
- null,
- 0,
- null,
- 1,
- barrier);
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs
deleted file mode 100644
index 0027753c..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanOptions.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- public class VulkanOptions
- {
- /// <summary>
- /// Sets the application name of the Vulkan instance
- /// </summary>
- public string ApplicationName { get; set; }
-
- /// <summary>
- /// Specifies additional extensions to enable if available on the instance
- /// </summary>
- public IEnumerable<string> InstanceExtensions { get; set; } = Enumerable.Empty<string>();
-
- /// <summary>
- /// Specifies layers to enable if available on the instance
- /// </summary>
- public IEnumerable<string> EnabledLayers { get; set; } = Enumerable.Empty<string>();
-
- /// <summary>
- /// Enables the debug layer
- /// </summary>
- public bool UseDebug { get; set; }
-
- /// <summary>
- /// Selects the first suitable discrete GPU available
- /// </summary>
- public bool PreferDiscreteGpu { get; set; }
-
- /// <summary>
- /// Sets the device to use if available and suitable.
- /// </summary>
- public string PreferredDevice { get; set; }
-
- /// <summary>
- /// Max number of device queues to request
- /// </summary>
- public uint MaxQueueCount { get; set; }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs
deleted file mode 100644
index 11444d30..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPhysicalDevice.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using Ryujinx.Graphics.Vulkan;
-using Silk.NET.Core;
-using Silk.NET.Vulkan;
-using Silk.NET.Vulkan.Extensions.KHR;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- public unsafe class VulkanPhysicalDevice
- {
- private VulkanPhysicalDevice(PhysicalDevice apiHandle, Vk api, uint queueCount, uint queueFamilyIndex)
- {
- InternalHandle = apiHandle;
- Api = api;
- QueueCount = queueCount;
- QueueFamilyIndex = queueFamilyIndex;
-
- api.GetPhysicalDeviceProperties(apiHandle, out var properties);
-
- DeviceName = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
- DeviceId = VulkanInitialization.StringFromIdPair(properties.VendorID, properties.DeviceID);
-
- var version = (Version32)properties.ApiVersion;
- ApiVersion = new Version((int)version.Major, (int)version.Minor, 0, (int)version.Patch);
- }
-
- internal PhysicalDevice InternalHandle { get; }
- internal Vk Api { get; }
- public uint QueueCount { get; }
- public uint QueueFamilyIndex { get; }
- public IntPtr Handle => InternalHandle.Handle;
-
- public string DeviceName { get; }
- public string DeviceId { get; }
- public Version ApiVersion { get; }
- public static Dictionary<PhysicalDevice, PhysicalDeviceProperties> PhysicalDevices { get; private set; }
- public static IEnumerable<KeyValuePair<PhysicalDevice, PhysicalDeviceProperties>> SuitableDevices { get; private set; }
-
- internal static void SelectAvailableDevices(VulkanInstance instance,
- VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice)
- {
- uint physicalDeviceCount;
-
- instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, null).ThrowOnError();
-
- var physicalDevices = new PhysicalDevice[physicalDeviceCount];
-
- fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
- {
- instance.Api.EnumeratePhysicalDevices(instance.InternalHandle, &physicalDeviceCount, pPhysicalDevices)
- .ThrowOnError();
- }
-
- PhysicalDevices = new Dictionary<PhysicalDevice, PhysicalDeviceProperties>();
-
- foreach (var physicalDevice in physicalDevices)
- {
- instance.Api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
- PhysicalDevices.Add(physicalDevice, properties);
- }
-
- SuitableDevices = PhysicalDevices.Where(x => IsSuitableDevice(
- instance.Api,
- x.Key,
- x.Value,
- surface.ApiHandle,
- out _,
- out _));
- }
-
- internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(VulkanInstance instance,
- VulkanSurface surface, bool preferDiscreteGpu, string preferredDevice)
- {
- SelectAvailableDevices(instance, surface, preferDiscreteGpu, preferredDevice);
-
- uint queueFamilyIndex = 0;
- uint queueCount = 0;
-
- if (!string.IsNullOrWhiteSpace(preferredDevice))
- {
- var physicalDevice = SuitableDevices.FirstOrDefault(x => VulkanInitialization.StringFromIdPair(x.Value.VendorID, x.Value.DeviceID) == preferredDevice);
-
- queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key,
- surface.ApiHandle, out queueCount);
- if (queueFamilyIndex != int.MaxValue)
- {
- return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex);
- }
- }
-
- if (preferDiscreteGpu)
- {
- var discreteGpus = SuitableDevices.Where(p => p.Value.DeviceType == PhysicalDeviceType.DiscreteGpu);
-
- foreach (var gpu in discreteGpus)
- {
- queueFamilyIndex = FindSuitableQueueFamily(instance.Api, gpu.Key,
- surface.ApiHandle, out queueCount);
- if (queueFamilyIndex != int.MaxValue)
- {
- return new VulkanPhysicalDevice(gpu.Key, instance.Api, queueCount, queueFamilyIndex);
- }
- }
- }
-
- foreach (var physicalDevice in SuitableDevices)
- {
- queueFamilyIndex = FindSuitableQueueFamily(instance.Api, physicalDevice.Key,
- surface.ApiHandle, out queueCount);
- if (queueFamilyIndex != int.MaxValue)
- {
- return new VulkanPhysicalDevice(physicalDevice.Key, instance.Api, queueCount, queueFamilyIndex);
- }
- }
-
- throw new Exception("No suitable physical device found");
- }
-
- private static unsafe bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, PhysicalDeviceProperties properties, SurfaceKHR surface,
- out uint queueCount, out uint familyIndex)
- {
- queueCount = 0;
- familyIndex = 0;
-
- if (properties.DeviceType == PhysicalDeviceType.Cpu) return false;
-
- var extensionMatches = 0;
- uint propertiesCount;
-
- api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
-
- var extensionProperties = new ExtensionProperties[propertiesCount];
-
- fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
- {
- api.EnumerateDeviceExtensionProperties(
- physicalDevice,
- (byte*)null,
- &propertiesCount,
- pExtensionProperties).ThrowOnError();
-
- for (var i = 0; i < propertiesCount; i++)
- {
- var extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName);
-
- if (VulkanInitialization.RequiredExtensions.Contains(extensionName))
- {
- extensionMatches++;
- }
- }
- }
-
- if (extensionMatches == VulkanInitialization.RequiredExtensions.Length)
- {
- familyIndex = FindSuitableQueueFamily(api, physicalDevice, surface, out queueCount);
-
- return familyIndex != uint.MaxValue;
- }
-
- return false;
- }
-
- internal unsafe string[] GetSupportedExtensions()
- {
- uint propertiesCount;
-
- Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, null).ThrowOnError();
-
- var extensionProperties = new ExtensionProperties[propertiesCount];
-
- fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
- {
- Api.EnumerateDeviceExtensionProperties(InternalHandle, (byte*)null, &propertiesCount, pExtensionProperties)
- .ThrowOnError();
- }
-
- return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
- }
-
- private static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface,
- out uint queueCount)
- {
- const QueueFlags RequiredFlags = QueueFlags.QueueGraphicsBit | QueueFlags.QueueComputeBit;
-
- var khrSurface = new KhrSurface(api.Context);
-
- uint propertiesCount;
-
- api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
-
- var properties = new QueueFamilyProperties[propertiesCount];
-
- fixed (QueueFamilyProperties* pProperties = properties)
- {
- api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties);
- }
-
- for (uint index = 0; index < propertiesCount; index++)
- {
- var queueFlags = properties[index].QueueFlags;
-
- khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported)
- .ThrowOnError();
-
- if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
- {
- queueCount = properties[index].QueueCount;
- return index;
- }
- }
-
- queueCount = 0;
- return uint.MaxValue;
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs
deleted file mode 100644
index ff8d9328..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanPlatformInterface.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using Avalonia;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Ryujinx.Graphics.Vulkan;
-using Silk.NET.Vulkan;
-using System;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanPlatformInterface : IDisposable
- {
- private static VulkanOptions _options;
-
- private VulkanPlatformInterface(VulkanInstance instance)
- {
- Instance = instance;
- Api = instance.Api;
- }
-
- public VulkanPhysicalDevice PhysicalDevice { get; private set; }
- public VulkanInstance Instance { get; }
- public Vk Api { get; private set; }
- public VulkanSurfaceRenderTarget MainSurface { get; set; }
-
- public void Dispose()
- {
- Instance?.Dispose();
- Api?.Dispose();
- }
-
- private static VulkanPlatformInterface TryCreate()
- {
- _options = AvaloniaLocator.Current.GetService<VulkanOptions>() ?? new VulkanOptions();
-
- var instance = VulkanInstance.Create(_options);
-
- return new VulkanPlatformInterface(instance);
- }
-
- public static bool TryInitialize()
- {
- var feature = TryCreate();
- if (feature != null)
- {
- AvaloniaLocator.CurrentMutable.Bind<VulkanPlatformInterface>().ToConstant(feature);
- return true;
- }
-
- return false;
- }
-
- public VulkanSurfaceRenderTarget CreateRenderTarget(IVulkanPlatformSurface platformSurface)
- {
- var surface = VulkanSurface.CreateSurface(Instance, platformSurface);
-
- if (PhysicalDevice == null)
- {
- PhysicalDevice = VulkanPhysicalDevice.FindSuitablePhysicalDevice(Instance, surface, _options.PreferDiscreteGpu, _options.PreferredDevice);
- }
-
- var renderTarget = new VulkanSurfaceRenderTarget(this, surface);
-
- if (MainSurface == null && surface != null)
- {
- MainSurface = renderTarget;
- }
-
- return renderTarget;
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs
deleted file mode 100644
index a903e21a..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanQueue.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanQueue
- {
- public VulkanQueue(VulkanDevice device, Queue apiHandle)
- {
- Device = device;
- InternalHandle = apiHandle;
- }
-
- public VulkanDevice Device { get; }
- public IntPtr Handle => InternalHandle.Handle;
- internal Queue InternalHandle { get; }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs
deleted file mode 100644
index 3b5fd9cc..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSemaphorePair.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanSemaphorePair : IDisposable
- {
- private readonly VulkanDevice _device;
-
- public unsafe VulkanSemaphorePair(VulkanDevice device)
- {
- _device = device;
-
- var semaphoreCreateInfo = new SemaphoreCreateInfo { SType = StructureType.SemaphoreCreateInfo };
-
- _device.Api.CreateSemaphore(_device.InternalHandle, semaphoreCreateInfo, null, out var semaphore).ThrowOnError();
- ImageAvailableSemaphore = semaphore;
-
- _device.Api.CreateSemaphore(_device.InternalHandle, semaphoreCreateInfo, null, out semaphore).ThrowOnError();
- RenderFinishedSemaphore = semaphore;
- }
-
- internal Semaphore ImageAvailableSemaphore { get; }
- internal Semaphore RenderFinishedSemaphore { get; }
-
- public unsafe void Dispose()
- {
- _device.Api.DestroySemaphore(_device.InternalHandle, ImageAvailableSemaphore, null);
- _device.Api.DestroySemaphore(_device.InternalHandle, RenderFinishedSemaphore, null);
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs
deleted file mode 100644
index 2452cdcd..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurface.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using Avalonia;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Silk.NET.Vulkan;
-using Silk.NET.Vulkan.Extensions.KHR;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- public class VulkanSurface : IDisposable
- {
- private readonly VulkanInstance _instance;
- private readonly IVulkanPlatformSurface _vulkanPlatformSurface;
-
- private VulkanSurface(IVulkanPlatformSurface vulkanPlatformSurface, VulkanInstance instance)
- {
- _vulkanPlatformSurface = vulkanPlatformSurface;
- _instance = instance;
- ApiHandle = vulkanPlatformSurface.CreateSurface(instance);
- }
-
- internal SurfaceKHR ApiHandle { get; }
-
- internal static KhrSurface SurfaceExtension { get; private set; }
-
- internal PixelSize SurfaceSize => _vulkanPlatformSurface.SurfaceSize;
-
- public void Dispose()
- {
- SurfaceExtension.DestroySurface(_instance.InternalHandle, ApiHandle, Span<AllocationCallbacks>.Empty);
- _vulkanPlatformSurface.Dispose();
- }
-
- internal static VulkanSurface CreateSurface(VulkanInstance instance, IVulkanPlatformSurface vulkanPlatformSurface)
- {
- if (SurfaceExtension == null)
- {
- instance.Api.TryGetInstanceExtension(instance.InternalHandle, out KhrSurface extension);
-
- SurfaceExtension = extension;
- }
-
- return new VulkanSurface(vulkanPlatformSurface, instance);
- }
-
- internal bool CanSurfacePresent(VulkanPhysicalDevice physicalDevice)
- {
- SurfaceExtension.GetPhysicalDeviceSurfaceSupport(physicalDevice.InternalHandle, physicalDevice.QueueFamilyIndex, ApiHandle, out var isSupported);
-
- return isSupported;
- }
-
- internal SurfaceFormatKHR GetSurfaceFormat(VulkanPhysicalDevice physicalDevice)
- {
- Span<uint> surfaceFormatsCount = stackalloc uint[1];
- SurfaceExtension.GetPhysicalDeviceSurfaceFormats(physicalDevice.InternalHandle, ApiHandle, surfaceFormatsCount, Span<SurfaceFormatKHR>.Empty);
- Span<SurfaceFormatKHR> surfaceFormats = stackalloc SurfaceFormatKHR[(int)surfaceFormatsCount[0]];
- SurfaceExtension.GetPhysicalDeviceSurfaceFormats(physicalDevice.InternalHandle, ApiHandle, surfaceFormatsCount, surfaceFormats);
-
- if (surfaceFormats.Length == 1 && surfaceFormats[0].Format == Format.Undefined)
- {
- return new SurfaceFormatKHR(Format.B8G8R8A8Unorm, ColorSpaceKHR.ColorspaceSrgbNonlinearKhr);
- }
-
- foreach (var format in surfaceFormats)
- {
- if (format.Format == Format.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.ColorspaceSrgbNonlinearKhr)
- {
- return format;
- }
- }
-
- return surfaceFormats[0];
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs b/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs
deleted file mode 100644
index 71f5f18a..00000000
--- a/Ryujinx.Ava/Ui/Backend/Vulkan/VulkanSurfaceRenderingSession.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using Avalonia;
-using Ryujinx.Ava.Ui.Vulkan.Surfaces;
-using Silk.NET.Vulkan;
-
-namespace Ryujinx.Ava.Ui.Vulkan
-{
- internal class VulkanSurfaceRenderingSession : IDisposable
- {
- private readonly VulkanDevice _device;
- private readonly VulkanSurfaceRenderTarget _renderTarget;
-
- public VulkanSurfaceRenderingSession(VulkanDisplay display, VulkanDevice device,
- VulkanSurfaceRenderTarget renderTarget, float scaling)
- {
- Display = display;
- _device = device;
- _renderTarget = renderTarget;
- Scaling = scaling;
- Begin();
- }
-
- public VulkanDisplay Display { get; }
-
- public PixelSize Size => _renderTarget.Size;
- public Vk Api => _device.Api;
-
- public float Scaling { get; }
-
- private void Begin()
- {
- if (!Display.EnsureSwapchainAvailable())
- {
- _renderTarget.RecreateImage();
- }
- }
-
- public void Dispose()
- {
- _renderTarget.EndDraw();
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs
index 15ecaa77..e774a09a 100644
--- a/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs
+++ b/Ryujinx.Ava/Ui/Controls/ContentDialogHelper.cs
@@ -1,4 +1,8 @@
+using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
@@ -27,9 +31,69 @@ namespace Ryujinx.Ava.Ui.Controls
{
UserResult result = UserResult.None;
- ContentDialog contentDialog = new ContentDialog();
+ bool useOverlay = false;
+ Window mainWindow = null;
- await ShowDialog();
+ if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
+ {
+ foreach (var item in al.Windows)
+ {
+ if (item.IsActive && item is MainWindow window && window.ViewModel.IsGameRunning)
+ {
+ mainWindow = window;
+ useOverlay = true;
+ break;
+ }
+ }
+ }
+
+ ContentDialog contentDialog = null;
+ ContentDialogOverlayWindow overlay = null;
+
+ if (useOverlay)
+ {
+ overlay = new ContentDialogOverlayWindow()
+ {
+ Height = mainWindow.Bounds.Height,
+ Width = mainWindow.Bounds.Width,
+ Position = mainWindow.PointToScreen(new Point())
+ };
+
+ mainWindow.PositionChanged += OverlayOnPositionChanged;
+
+ void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
+ {
+ overlay.Position = mainWindow.PointToScreen(new Point());
+ }
+
+ contentDialog = overlay.ContentDialog;
+
+ bool opened = false;
+
+ overlay.Opened += OverlayOnActivated;
+
+ async void OverlayOnActivated(object sender, EventArgs e)
+ {
+ if (opened)
+ {
+ return;
+ }
+
+ opened = true;
+
+ overlay.Position = mainWindow.PointToScreen(new Point());
+
+ await ShowDialog();
+ }
+
+ await overlay.ShowDialog(mainWindow);
+ }
+ else
+ {
+ contentDialog = new ContentDialog();
+
+ await ShowDialog();
+ }
async Task ShowDialog()
{
@@ -53,6 +117,14 @@ namespace Ryujinx.Ava.Ui.Controls
});
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
+
+ overlay?.Close();
+ }
+
+ if (useOverlay)
+ {
+ overlay.Content = null;
+ overlay.Close();
}
return result;
@@ -323,4 +395,4 @@ namespace Ryujinx.Ava.Ui.Controls
return string.Empty;
}
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs
new file mode 100644
index 00000000..d9fae93a
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/EmbeddedWindow.cs
@@ -0,0 +1,204 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Platform;
+using SPB.Graphics;
+using SPB.Platform;
+using SPB.Platform.GLX;
+using SPB.Platform.X11;
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Threading.Tasks;
+using static Ryujinx.Ava.Ui.Controls.Win32NativeInterop;
+
+namespace Ryujinx.Ava.Ui.Controls
+{
+ public unsafe class EmbeddedWindow : NativeControlHost
+ {
+ private WindowProc _wndProcDelegate;
+ private string _className;
+
+ protected GLXWindow X11Window { get; private set; }
+ protected IntPtr WindowHandle { get; set; }
+ protected IntPtr X11Display { get; set; }
+
+ public event EventHandler<IntPtr> WindowCreated;
+ public event EventHandler<Size> SizeChanged;
+
+ protected virtual void OnWindowDestroyed() { }
+ protected virtual void OnWindowDestroying()
+ {
+ WindowHandle = IntPtr.Zero;
+ X11Display = IntPtr.Zero;
+ }
+
+ public EmbeddedWindow()
+ {
+ var stateObserverable = this.GetObservable(Control.BoundsProperty);
+
+ stateObserverable.Subscribe(StateChanged);
+
+ this.Initialized += NativeEmbeddedWindow_Initialized;
+ }
+
+ public virtual void OnWindowCreated() { }
+
+ private void NativeEmbeddedWindow_Initialized(object sender, EventArgs e)
+ {
+ OnWindowCreated();
+
+ Task.Run(() =>
+ {
+ WindowCreated?.Invoke(this, WindowHandle);
+ });
+ }
+
+ private void StateChanged(Rect rect)
+ {
+ SizeChanged?.Invoke(this, rect.Size);
+ }
+
+ protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
+ {
+ if (OperatingSystem.IsLinux())
+ {
+ return CreateLinux(parent);
+ }
+ else if (OperatingSystem.IsWindows())
+ {
+ return CreateWin32(parent);
+ }
+ return base.CreateNativeControlCore(parent);
+ }
+
+ protected override void DestroyNativeControlCore(IPlatformHandle control)
+ {
+ OnWindowDestroying();
+
+ if (OperatingSystem.IsLinux())
+ {
+ DestroyLinux();
+ }
+ else if (OperatingSystem.IsWindows())
+ {
+ DestroyWin32(control);
+ }
+ else
+ {
+ base.DestroyNativeControlCore(control);
+ }
+
+ OnWindowDestroyed();
+ }
+
+ [SupportedOSPlatform("linux")]
+ IPlatformHandle CreateLinux(IPlatformHandle parent)
+ {
+ X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
+
+ WindowHandle = X11Window.WindowHandle.RawHandle;
+
+ X11Display = X11Window.DisplayHandle.RawHandle;
+
+ return new PlatformHandle(WindowHandle, "X11");
+ }
+
+ [SupportedOSPlatform("windows")]
+ unsafe IPlatformHandle CreateWin32(IPlatformHandle parent)
+ {
+ _className = "NativeWindow-" + Guid.NewGuid();
+ _wndProcDelegate = WndProc;
+ var wndClassEx = new WNDCLASSEX
+ {
+ cbSize = Marshal.SizeOf<WNDCLASSEX>(),
+ hInstance = GetModuleHandle(null),
+ lpfnWndProc = _wndProcDelegate,
+ style = ClassStyles.CS_OWNDC,
+ lpszClassName = _className,
+ hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
+ };
+
+ var atom = RegisterClassEx(ref wndClassEx);
+
+ var handle = CreateWindowEx(
+ 0,
+ _className,
+ "NativeWindow",
+ WindowStyles.WS_CHILD,
+ 0,
+ 0,
+ 640,
+ 480,
+ parent.Handle,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ IntPtr.Zero);
+
+ WindowHandle = handle;
+
+ return new PlatformHandle(WindowHandle, "HWND");
+ }
+
+ [SupportedOSPlatform("windows")]
+ internal IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
+ {
+ var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
+ var root = VisualRoot as Window;
+ bool isLeft = false;
+ switch (msg)
+ {
+ case WindowsMessages.LBUTTONDOWN:
+ case WindowsMessages.RBUTTONDOWN:
+ isLeft = msg == WindowsMessages.LBUTTONDOWN;
+ this.RaiseEvent(new PointerPressedEventArgs(
+ this,
+ new Avalonia.Input.Pointer(0, PointerType.Mouse, true),
+ root,
+ this.TranslatePoint(point, root).Value,
+ (ulong)Environment.TickCount64,
+ new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed),
+ KeyModifiers.None));
+ break;
+ case WindowsMessages.LBUTTONUP:
+ case WindowsMessages.RBUTTONUP:
+ isLeft = msg == WindowsMessages.LBUTTONUP;
+ this.RaiseEvent(new PointerReleasedEventArgs(
+ this,
+ new Avalonia.Input.Pointer(0, PointerType.Mouse, true),
+ root,
+ this.TranslatePoint(point, root).Value,
+ (ulong)Environment.TickCount64,
+ new PointerPointProperties(isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased),
+ KeyModifiers.None,
+ isLeft ? MouseButton.Left : MouseButton.Right));
+ break;
+ case WindowsMessages.MOUSEMOVE:
+ this.RaiseEvent(new PointerEventArgs(
+ PointerMovedEvent,
+ this,
+ new Avalonia.Input.Pointer(0, PointerType.Mouse, true),
+ root,
+ this.TranslatePoint(point, root).Value,
+ (ulong)Environment.TickCount64,
+ new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
+ KeyModifiers.None));
+ break;
+ }
+ return DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam);
+ }
+
+ void DestroyLinux()
+ {
+ X11Window?.Dispose();
+ }
+
+ [SupportedOSPlatform("windows")]
+ void DestroyWin32(IPlatformHandle handle)
+ {
+ DestroyWindow(handle.Handle);
+ UnregisterClass(_className, GetModuleHandle(null));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs
new file mode 100644
index 00000000..ce579fdf
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs
@@ -0,0 +1,85 @@
+using Avalonia;
+using Avalonia.OpenGL;
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common.Configuration;
+using SPB.Graphics;
+using SPB.Graphics.OpenGL;
+using SPB.Platform;
+using SPB.Platform.GLX;
+using SPB.Platform.WGL;
+using SPB.Windowing;
+using System;
+
+namespace Ryujinx.Ava.Ui.Controls
+{
+ public class OpenGLEmbeddedWindow : EmbeddedWindow
+ {
+ private readonly int _major;
+ private readonly int _minor;
+ private readonly GraphicsDebugLevel _graphicsDebugLevel;
+ private SwappableNativeWindowBase _window;
+ public OpenGLContextBase Context { get; set; }
+
+ public OpenGLEmbeddedWindow(int major, int minor, GraphicsDebugLevel graphicsDebugLevel)
+ {
+ _major = major;
+ _minor = minor;
+ _graphicsDebugLevel = graphicsDebugLevel;
+ }
+
+ protected override void OnWindowDestroying()
+ {
+ Context.Dispose();
+ base.OnWindowDestroying();
+ }
+
+ public override void OnWindowCreated()
+ {
+ base.OnWindowCreated();
+
+ if (OperatingSystem.IsWindows())
+ {
+ _window = new WGLWindow(new NativeHandle(WindowHandle));
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ _window = X11Window;
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ var flags = OpenGLContextFlags.Compat;
+ if (_graphicsDebugLevel != GraphicsDebugLevel.None)
+ {
+ flags |= OpenGLContextFlags.Debug;
+ }
+
+ Context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, _major, _minor, flags);
+
+ Context.Initialize(_window);
+ Context.MakeCurrent(_window);
+
+ var bindingsContext = new OpenToolkitBindingsContext(Context.GetProcAddress);
+
+ GL.LoadBindings(bindingsContext);
+ Context.MakeCurrent(null);
+ }
+
+ public void MakeCurrent()
+ {
+ Context.MakeCurrent(_window);
+ }
+
+ public void MakeCurrent(NativeWindowBase window)
+ {
+ Context.MakeCurrent(window);
+ }
+
+ public void SwapBuffers()
+ {
+ _window.SwapBuffers();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs
deleted file mode 100644
index e58bdaa0..00000000
--- a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using Avalonia;
-using Avalonia.OpenGL;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Skia;
-using Avalonia.Threading;
-using OpenTK.Graphics.OpenGL;
-using Ryujinx.Common.Configuration;
-using SkiaSharp;
-using SPB.Graphics;
-using SPB.Graphics.OpenGL;
-using SPB.Platform;
-using SPB.Windowing;
-using System;
-
-namespace Ryujinx.Ava.Ui.Controls
-{
- internal class OpenGLRendererControl : RendererControl
- {
- public int Major { get; }
- public int Minor { get; }
- public OpenGLContextBase GameContext { get; set; }
-
- public static OpenGLContextBase PrimaryContext =>
- AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>()
- .PrimaryContext.AsOpenGLContextBase();
-
- private SwappableNativeWindowBase _gameBackgroundWindow;
-
- private IntPtr _fence;
-
- public OpenGLRendererControl(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel)
- {
- Major = major;
- Minor = minor;
- }
-
- public override void DestroyBackgroundContext()
- {
- Image = null;
-
- if (_fence != IntPtr.Zero)
- {
- DrawOperation.Dispose();
- GL.DeleteSync(_fence);
- }
-
- GlDrawOperation.DeleteFramebuffer();
-
- GameContext?.Dispose();
-
- _gameBackgroundWindow?.Dispose();
- }
-
- internal override void Present(object image)
- {
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- Image = (int)image;
-
- InvalidateVisual();
- }).Wait();
-
- if (_fence != IntPtr.Zero)
- {
- GL.DeleteSync(_fence);
- }
-
- _fence = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None);
-
- InvalidateVisual();
-
- _gameBackgroundWindow.SwapBuffers();
- }
-
- internal override void MakeCurrent()
- {
- GameContext.MakeCurrent(_gameBackgroundWindow);
- }
-
- internal override void MakeCurrent(SwappableNativeWindowBase window)
- {
- GameContext.MakeCurrent(window);
- }
-
- protected override void CreateWindow()
- {
- var flags = OpenGLContextFlags.Compat;
- if (DebugLevel != GraphicsDebugLevel.None)
- {
- flags |= OpenGLContextFlags.Debug;
- }
- _gameBackgroundWindow = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
- _gameBackgroundWindow.Hide();
-
- GameContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, Major, Minor, flags, shareContext: PrimaryContext);
- GameContext.Initialize(_gameBackgroundWindow);
- }
-
- protected override ICustomDrawOperation CreateDrawOperation()
- {
- return new GlDrawOperation(this);
- }
-
- private class GlDrawOperation : ICustomDrawOperation
- {
- private static int _framebuffer;
-
- public Rect Bounds { get; }
-
- private readonly OpenGLRendererControl _control;
-
- public GlDrawOperation(OpenGLRendererControl control)
- {
- _control = control;
- Bounds = _control.Bounds;
- }
-
- public void Dispose() { }
-
- public static void DeleteFramebuffer()
- {
- if (_framebuffer == 0)
- {
- GL.DeleteFramebuffer(_framebuffer);
- }
-
- _framebuffer = 0;
- }
-
- public bool Equals(ICustomDrawOperation other)
- {
- return other is GlDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds;
- }
-
- public bool HitTest(Point p)
- {
- return Bounds.Contains(p);
- }
-
- private void CreateRenderTarget()
- {
- _framebuffer = GL.GenFramebuffer();
- }
-
- public void Render(IDrawingContextImpl context)
- {
- if (_control.Image == null)
- {
- return;
- }
-
- if (_framebuffer == 0)
- {
- CreateRenderTarget();
- }
-
- int currentFramebuffer = GL.GetInteger(GetPName.FramebufferBinding);
-
- var image = _control.Image;
- var fence = _control._fence;
-
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer);
- GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, (int)image, 0);
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, currentFramebuffer);
-
- if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl)
- {
- return;
- }
-
- var imageInfo = new SKImageInfo((int)_control.RenderSize.Width, (int)_control.RenderSize.Height, SKColorType.Rgba8888);
- var glInfo = new GRGlFramebufferInfo((uint)_framebuffer, SKColorType.Rgba8888.ToGlSizedFormat());
-
- GL.WaitSync(fence, WaitSyncFlags.None, ulong.MaxValue);
-
- using var backendTexture = new GRBackendRenderTarget(imageInfo.Width, imageInfo.Height, 1, 0, glInfo);
- using var surface = SKSurface.Create(skiaDrawingContextImpl.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
-
- if (surface == null)
- {
- return;
- }
-
- var rect = new Rect(new Point(), _control.RenderSize);
-
- using var snapshot = surface.Snapshot();
- skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint());
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Controls/RendererControl.cs b/Ryujinx.Ava/Ui/Controls/RendererControl.cs
deleted file mode 100644
index 392f67e3..00000000
--- a/Ryujinx.Ava/Ui/Controls/RendererControl.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Media;
-using Avalonia.Rendering.SceneGraph;
-using Ryujinx.Common.Configuration;
-using SPB.Windowing;
-using System;
-
-namespace Ryujinx.Ava.Ui.Controls
-{
- internal abstract class RendererControl : Control
- {
- protected object Image { get; set; }
-
- public event EventHandler<EventArgs> RendererInitialized;
- public event EventHandler<Size> SizeChanged;
-
- protected Size RenderSize { get; private set; }
- public bool IsStarted { get; private set; }
-
- public GraphicsDebugLevel DebugLevel { get; }
-
- private bool _isInitialized;
-
- protected ICustomDrawOperation DrawOperation { get; private set; }
-
- public RendererControl(GraphicsDebugLevel graphicsDebugLevel)
- {
- DebugLevel = graphicsDebugLevel;
- IObservable<Rect> resizeObservable = this.GetObservable(BoundsProperty);
-
- resizeObservable.Subscribe(Resized);
-
- Focusable = true;
- }
-
- protected void Resized(Rect rect)
- {
- SizeChanged?.Invoke(this, rect.Size);
-
- if (!rect.IsEmpty)
- {
- RenderSize = rect.Size * VisualRoot.RenderScaling;
- DrawOperation = CreateDrawOperation();
- }
- }
-
- protected abstract ICustomDrawOperation CreateDrawOperation();
- protected abstract void CreateWindow();
-
- public override void Render(DrawingContext context)
- {
- if (!_isInitialized)
- {
- CreateWindow();
-
- OnRendererInitialized();
- _isInitialized = true;
- }
-
- if (!IsStarted || Image == null)
- {
- return;
- }
-
- if (DrawOperation != null)
- {
- context.Custom(DrawOperation);
- }
-
- base.Render(context);
- }
-
- protected void OnRendererInitialized()
- {
- RendererInitialized?.Invoke(this, EventArgs.Empty);
- }
-
- internal abstract void Present(object image);
-
- internal void Start()
- {
- IsStarted = true;
- }
-
- internal void Stop()
- {
- IsStarted = false;
- }
-
- public abstract void DestroyBackgroundContext();
- internal abstract void MakeCurrent();
- internal abstract void MakeCurrent(SwappableNativeWindowBase window);
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml
new file mode 100644
index 00000000..be72fd61
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml
@@ -0,0 +1,14 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="Ryujinx.Ava.Ui.Controls.RendererHost">
+ <ContentControl
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Stretch"
+ VerticalAlignment="Stretch"
+ VerticalContentAlignment="Stretch"
+ Name="View"
+ />
+</UserControl>
diff --git a/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs
new file mode 100644
index 00000000..0d1984fd
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/RendererHost.axaml.cs
@@ -0,0 +1,126 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Ryujinx.Common.Configuration;
+using Silk.NET.Vulkan;
+using SPB.Graphics.OpenGL;
+using SPB.Windowing;
+using System;
+
+namespace Ryujinx.Ava.Ui.Controls
+{
+ public partial class RendererHost : UserControl, IDisposable
+ {
+ private readonly GraphicsDebugLevel _graphicsDebugLevel;
+ private EmbeddedWindow _currentWindow;
+
+ public bool IsVulkan { get; private set; }
+
+ public RendererHost(GraphicsDebugLevel graphicsDebugLevel)
+ {
+ _graphicsDebugLevel = graphicsDebugLevel;
+ InitializeComponent();
+ }
+
+ public RendererHost()
+ {
+ InitializeComponent();
+ }
+
+ public void CreateOpenGL()
+ {
+ Dispose();
+
+ _currentWindow = new OpenGLEmbeddedWindow(3, 3, _graphicsDebugLevel);
+ Initialize();
+
+ IsVulkan = false;
+ }
+
+ private void Initialize()
+ {
+ _currentWindow.WindowCreated += CurrentWindow_WindowCreated;
+ _currentWindow.SizeChanged += CurrentWindow_SizeChanged;
+ View.Content = _currentWindow;
+ }
+
+ public void CreateVulkan()
+ {
+ Dispose();
+
+ _currentWindow = new VulkanEmbeddedWindow();
+ Initialize();
+
+ IsVulkan = true;
+ }
+
+ public OpenGLContextBase GetContext()
+ {
+ if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
+ {
+ return openGlEmbeddedWindow.Context;
+ }
+
+ return null;
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ Dispose();
+ }
+
+ private void CurrentWindow_SizeChanged(object sender, Size e)
+ {
+ SizeChanged?.Invoke(sender, e);
+ }
+
+ private void CurrentWindow_WindowCreated(object sender, IntPtr e)
+ {
+ RendererInitialized?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void MakeCurrent()
+ {
+ if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
+ {
+ openGlEmbeddedWindow.MakeCurrent();
+ }
+ }
+
+ public void MakeCurrent(SwappableNativeWindowBase window)
+ {
+ if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
+ {
+ openGlEmbeddedWindow.MakeCurrent(window);
+ }
+ }
+
+ public void SwapBuffers()
+ {
+ if (_currentWindow is OpenGLEmbeddedWindow openGlEmbeddedWindow)
+ {
+ openGlEmbeddedWindow.SwapBuffers();
+ }
+ }
+
+ public event EventHandler<EventArgs> RendererInitialized;
+ public event Action<object, Size> SizeChanged;
+ public void Dispose()
+ {
+ if (_currentWindow != null)
+ {
+ _currentWindow.WindowCreated -= CurrentWindow_WindowCreated;
+ _currentWindow.SizeChanged -= CurrentWindow_SizeChanged;
+ }
+ }
+
+ public SurfaceKHR CreateVulkanSurface(Instance instance, Vk api)
+ {
+ return (_currentWindow is VulkanEmbeddedWindow vulkanEmbeddedWindow)
+ ? vulkanEmbeddedWindow.CreateSurface(instance)
+ : default;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs
new file mode 100644
index 00000000..d2c980dd
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/VulkanEmbeddedWindow.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Ava.Ui.Controls;
+using Silk.NET.Vulkan;
+using SPB.Graphics.Vulkan;
+using SPB.Platform.Win32;
+using SPB.Platform.X11;
+using SPB.Windowing;
+using System;
+
+namespace Ryujinx.Ava.Ui
+{
+ public class VulkanEmbeddedWindow : EmbeddedWindow
+ {
+ private NativeWindowBase _window;
+
+ public SurfaceKHR CreateSurface(Instance instance)
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ _window = new SimpleWin32Window(new NativeHandle(WindowHandle));
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ _window = X11Window;
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, _window));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs
deleted file mode 100644
index 7b7dfaa1..00000000
--- a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using Avalonia;
-using Avalonia.Media;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Skia;
-using Avalonia.Threading;
-using Ryujinx.Ava.Ui.Backend.Vulkan;
-using Ryujinx.Ava.Ui.Vulkan;
-using Ryujinx.Common.Configuration;
-using Ryujinx.Graphics.Vulkan;
-using Silk.NET.Vulkan;
-using SkiaSharp;
-using SPB.Windowing;
-using System;
-using System.Collections.Concurrent;
-
-namespace Ryujinx.Ava.Ui.Controls
-{
- internal class VulkanRendererControl : RendererControl
- {
- private const int MaxImagesInFlight = 3;
-
- private VulkanPlatformInterface _platformInterface;
- private ConcurrentQueue<PresentImageInfo> _imagesInFlight;
- private PresentImageInfo _currentImage;
-
- public VulkanRendererControl(GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel)
- {
- _platformInterface = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
-
- _imagesInFlight = new ConcurrentQueue<PresentImageInfo>();
- }
-
- public override void DestroyBackgroundContext()
- {
-
- }
-
- protected override ICustomDrawOperation CreateDrawOperation()
- {
- return new VulkanDrawOperation(this);
- }
-
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnDetachedFromVisualTree(e);
-
- _imagesInFlight.Clear();
-
- if (_platformInterface.MainSurface.Display != null)
- {
- _platformInterface.MainSurface.Display.Presented -= Window_Presented;
- }
-
- _currentImage?.Put();
- _currentImage = null;
- }
-
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnAttachedToVisualTree(e);
-
- _platformInterface.MainSurface.Display.Presented += Window_Presented;
- }
-
- private void Window_Presented(object sender, EventArgs e)
- {
- _platformInterface.MainSurface.Device.QueueWaitIdle();
- _currentImage?.Put();
- _currentImage = null;
- }
-
- public override void Render(DrawingContext context)
- {
- base.Render(context);
- }
-
- protected override void CreateWindow()
- {
- }
-
- internal override void MakeCurrent()
- {
- }
-
- internal override void MakeCurrent(SwappableNativeWindowBase window)
- {
- }
-
- internal override void Present(object image)
- {
- Image = image;
-
- _imagesInFlight.Enqueue((PresentImageInfo)image);
-
- if (_imagesInFlight.Count > MaxImagesInFlight)
- {
- _imagesInFlight.TryDequeue(out _);
- }
-
- Dispatcher.UIThread.Post(InvalidateVisual);
- }
-
- private PresentImageInfo GetImage()
- {
- lock (_imagesInFlight)
- {
- if (!_imagesInFlight.TryDequeue(out _currentImage))
- {
- _currentImage = (PresentImageInfo)Image;
- }
-
- return _currentImage;
- }
- }
-
- private class VulkanDrawOperation : ICustomDrawOperation
- {
- public Rect Bounds { get; }
-
- private readonly VulkanRendererControl _control;
- private bool _isDestroyed;
-
- public VulkanDrawOperation(VulkanRendererControl control)
- {
- _control = control;
- Bounds = _control.Bounds;
- }
-
- public void Dispose()
- {
- if (_isDestroyed)
- {
- return;
- }
-
- _isDestroyed = true;
- }
-
- public bool Equals(ICustomDrawOperation other)
- {
- return other is VulkanDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds;
- }
-
- public bool HitTest(Point p)
- {
- return Bounds.Contains(p);
- }
-
- public unsafe void Render(IDrawingContextImpl context)
- {
- if (_isDestroyed || _control.Image == null || _control.RenderSize.Width == 0 || _control.RenderSize.Height == 0 ||
- context is not ISkiaDrawingContextImpl skiaDrawingContextImpl)
- {
- return;
- }
-
- var image = _control.GetImage();
-
- if (!image.State.IsValid)
- {
- _control._currentImage = null;
-
- return;
- }
-
- var gpu = AvaloniaLocator.Current.GetService<VulkanSkiaGpu>();
-
- image.Get();
-
- var imageInfo = new GRVkImageInfo()
- {
- CurrentQueueFamily = _control._platformInterface.PhysicalDevice.QueueFamilyIndex,
- Format = (uint)Format.R8G8B8A8Unorm,
- Image = image.Image.Handle,
- ImageLayout = (uint)ImageLayout.TransferSrcOptimal,
- ImageTiling = (uint)ImageTiling.Optimal,
- ImageUsageFlags = (uint)(ImageUsageFlags.ImageUsageColorAttachmentBit
- | ImageUsageFlags.ImageUsageTransferSrcBit
- | ImageUsageFlags.ImageUsageTransferDstBit),
- LevelCount = 1,
- SampleCount = 1,
- Protected = false,
- Alloc = new GRVkAlloc()
- {
- Memory = image.Memory.Handle,
- Flags = 0,
- Offset = image.MemoryOffset,
- Size = image.MemorySize
- }
- };
-
- using var backendTexture = new GRBackendRenderTarget(
- (int)image.Extent.Width,
- (int)image.Extent.Height,
- 1,
- imageInfo);
-
- var vulkan = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
-
- using var surface = SKSurface.Create(
- skiaDrawingContextImpl.GrContext,
- backendTexture,
- GRSurfaceOrigin.TopLeft,
- SKColorType.Rgba8888);
-
- if (surface == null)
- {
- return;
- }
-
- var rect = new Rect(new Point(), new Size(image.Extent.Width, image.Extent.Height));
-
- using var snapshot = surface.Snapshot();
- skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(),
- new SKPaint());
- }
- }
- }
-}
diff --git a/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs b/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs
new file mode 100644
index 00000000..124536d9
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
+namespace Ryujinx.Ava.Ui.Controls
+{
+ [SupportedOSPlatform("windows")]
+ internal class Win32NativeInterop
+ {
+ [Flags]
+ public enum ClassStyles : uint
+ {
+ CS_CLASSDC = 0x40,
+ CS_OWNDC = 0x20,
+ }
+
+ [Flags]
+ public enum WindowStyles : uint
+ {
+ WS_CHILD = 0x40000000
+ }
+
+ public enum Cursors : uint
+ {
+ IDC_ARROW = 32512
+ }
+
+ public enum WindowsMessages : uint
+ {
+ MOUSEMOVE = 0x0200,
+ LBUTTONDOWN = 0x0201,
+ LBUTTONUP = 0x0202,
+ LBUTTONDBLCLK = 0x0203,
+ RBUTTONDOWN = 0x0204,
+ RBUTTONUP = 0x0205,
+ RBUTTONDBLCLK = 0x0206,
+ MBUTTONDOWN = 0x0207,
+ MBUTTONUP = 0x0208,
+ MBUTTONDBLCLK = 0x0209,
+ MOUSEWHEEL = 0x020A,
+ XBUTTONDOWN = 0x020B,
+ XBUTTONUP = 0x020C,
+ XBUTTONDBLCLK = 0x020D,
+ MOUSEHWHEEL = 0x020E,
+ MOUSELAST = 0x020E
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct WNDCLASSEX
+ {
+ public int cbSize;
+ public ClassStyles style;
+ [MarshalAs(UnmanagedType.FunctionPtr)]
+ public WindowProc lpfnWndProc; // not WndProc
+ public int cbClsExtra;
+ public int cbWndExtra;
+ public IntPtr hInstance;
+ public IntPtr hIcon;
+ public IntPtr hCursor;
+ public IntPtr hbrBackground;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string lpszMenuName;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string lpszClassName;
+ public IntPtr hIconSm;
+
+ public static WNDCLASSEX Create()
+ {
+ return new WNDCLASSEX
+ {
+ cbSize = Marshal.SizeOf<WNDCLASSEX>()
+ };
+ }
+ }
+
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern ushort RegisterClassEx(ref WNDCLASSEX param);
+
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ public static extern IntPtr DefWindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DestroyWindow(IntPtr hwnd);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
+
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr CreateWindowEx(
+ uint dwExStyle,
+ string lpClassName,
+ string lpWindowName,
+ WindowStyles dwStyle,
+ int x,
+ int y,
+ int nWidth,
+ int nHeight,
+ IntPtr hWndParent,
+ IntPtr hMenu,
+ IntPtr hInstance,
+ IntPtr lpParam);
+ }
+}
diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
index 32f08ff9..10dd2da3 100644
--- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
+++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
@@ -10,7 +10,6 @@ using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
-using Ryujinx.Ava.Ui.Vulkan;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
@@ -252,34 +251,19 @@ namespace Ryujinx.Ava.Ui.ViewModels
{
_gpuIds = new List<string>();
List<string> names = new List<string>();
- if (!Program.UseVulkan)
- {
- var devices = VulkanRenderer.GetPhysicalDevices();
+ var devices = VulkanRenderer.GetPhysicalDevices();
- if (devices.Length == 0)
- {
- IsVulkanAvailable = false;
- GraphicsBackendIndex = 1;
- }
- else
- {
- foreach (var device in devices)
- {
- _gpuIds.Add(device.Id);
- names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}");
- }
- }
+ if (devices.Length == 0)
+ {
+ IsVulkanAvailable = false;
+ GraphicsBackendIndex = 1;
}
else
{
- foreach (var device in VulkanPhysicalDevice.SuitableDevices)
+ foreach (var device in devices)
{
- _gpuIds.Add(
- VulkanInitialization.StringFromIdPair(device.Value.VendorID, device.Value.DeviceID));
- var value = device.Value;
- var name = value.DeviceName;
- names.Add(
- $"{Marshal.PtrToStringAnsi((IntPtr)name)} {(device.Value.DeviceType == PhysicalDeviceType.DiscreteGpu ? "(dGPU)" : "")}");
+ _gpuIds.Add(device.Id);
+ names.Add($"{device.Name} {(device.IsDiscrete ? "(dGPU)" : "")}");
}
}
@@ -407,7 +391,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
_previousVolumeLevel = Volume;
}
- public async Task SaveSettings()
+ public void SaveSettings()
{
ConfigurationState config = ConfigurationState.Instance;
@@ -422,8 +406,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
config.System.TimeZone.Value = TimeZone;
}
- bool requiresRestart = config.Graphics.GraphicsBackend.Value != (GraphicsBackend)GraphicsBackendIndex;
-
config.Logger.EnableError.Value = EnableError;
config.Logger.EnableTrace.Value = EnableTrace;
config.Logger.EnableWarn.Value = EnableWarn;
@@ -456,19 +438,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
config.System.Language.Value = (Language)Language;
config.System.Region.Value = (Region)Region;
- var selectedGpu = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
- if (!requiresRestart)
- {
- var platform = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>();
- if (platform != null)
- {
- var physicalDevice = platform.PhysicalDevice;
-
- requiresRestart = physicalDevice.DeviceId != selectedGpu;
- }
- }
-
- config.Graphics.PreferredGpu.Value = selectedGpu;
+ config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
{
@@ -507,20 +477,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
MainWindow.UpdateGraphicsConfig();
_previousVolumeLevel = Volume;
-
- if (requiresRestart)
- {
- var choice = await ContentDialogHelper.CreateChoiceDialog(
- LocaleManager.Instance["SettingsAppRequiredRestartMessage"],
- LocaleManager.Instance["SettingsGpuBackendRestartMessage"],
- LocaleManager.Instance["SettingsGpuBackendRestartSubMessage"]);
-
- if (choice)
- {
- Process.Start(Environment.ProcessPath);
- Environment.Exit(0);
- }
- }
}
public void RevertIfNotSaved()
diff --git a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml
new file mode 100644
index 00000000..2967f4f2
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml
@@ -0,0 +1,27 @@
+<window:StyleableWindow xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ d:DesignWidth="800"
+ d:DesignHeight="450"
+ xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ x:Class="Ryujinx.Ava.Ui.Windows.ContentDialogOverlayWindow"
+ xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
+ Title="ContentDialogOverlayWindow">
+ <window:StyleableWindow.Styles>
+ <Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot">
+ <Setter Property="Background"
+ Value="Transparent" />
+ </Style>
+ </window:StyleableWindow.Styles>
+ <ContentControl
+ Focusable="False"
+ IsVisible="False"
+ KeyboardNavigation.IsTabStop="False">
+ <ui:ContentDialog Name="ContentDialog"
+ IsPrimaryButtonEnabled="True"
+ IsSecondaryButtonEnabled="True"
+ IsVisible="False" />
+ </ContentControl>
+</window:StyleableWindow>
diff --git a/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs
new file mode 100644
index 00000000..7a51e64d
--- /dev/null
+++ b/Ryujinx.Ava/Ui/Windows/ContentDialogOverlayWindow.axaml.cs
@@ -0,0 +1,25 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+
+namespace Ryujinx.Ava.Ui.Windows
+{
+ public partial class ContentDialogOverlayWindow : StyleableWindow
+ {
+ public ContentDialogOverlayWindow()
+ {
+ InitializeComponent();
+#if DEBUG
+ this.AttachDevTools();
+#endif
+ ExtendClientAreaToDecorationsHint = true;
+ TransparencyLevelHint = WindowTransparencyLevel.Transparent;
+ WindowStartupLocation = WindowStartupLocation.Manual;
+ SystemDecorations = SystemDecorations.None;
+ ExtendClientAreaTitleBarHeightHint = 0;
+ Background = Brushes.Transparent;
+ CanResize = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml
index 610281a6..6eafd5e5 100644
--- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml
+++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml
@@ -462,6 +462,7 @@
VerticalAlignment="Stretch"
Background="{DynamicResource ThemeContentBackgroundColor}"
IsVisible="{Binding ShowLoadProgress}"
+ Name="LoadingView"
ZIndex="1000">
<Grid
Margin="40"
diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
index 6ce1d17d..976c2817 100644
--- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.Ava.Ui.Windows
internal AppHost AppHost { get; private set; }
public InputManager InputManager { get; private set; }
- internal RendererControl RendererControl { get; private set; }
+ internal RendererHost RendererControl { get; private set; }
internal MainWindowViewModel ViewModel { get; private set; }
public SettingsWindow SettingsWindow { get; set; }
@@ -237,7 +237,16 @@ namespace Ryujinx.Ava.Ui.Windows
_mainViewContent = MainContent.Content as Control;
- RendererControl = Program.UseVulkan ? new VulkanRendererControl(ConfigurationState.Instance.Logger.GraphicsDebugLevel) : new OpenGLRendererControl(3, 3, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
+ RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel);
+ if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
+ {
+ RendererControl.CreateOpenGL();
+ }
+ else
+ {
+ RendererControl.CreateVulkan();
+ }
+
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
if (!AppHost.LoadGuestApplication().Result)
@@ -296,8 +305,8 @@ namespace Ryujinx.Ava.Ui.Windows
public void SwitchToGameControl(bool startFullscreen = false)
{
- ViewModel.ShowContent = true;
ViewModel.ShowLoadProgress = false;
+ ViewModel.ShowContent = true;
ViewModel.IsLoadingIndeterminate = false;
Dispatcher.UIThread.InvokeAsync(() =>
@@ -346,17 +355,17 @@ namespace Ryujinx.Ava.Ui.Windows
Dispatcher.UIThread.InvokeAsync(() =>
{
- if (MainContent.Content != _mainViewContent)
- {
- MainContent.Content = _mainViewContent;
- }
-
ViewModel.ShowMenuAndStatusBar = true;
ViewModel.ShowContent = true;
ViewModel.ShowLoadProgress = false;
ViewModel.IsLoadingIndeterminate = false;
Cursor = Cursor.Default;
+ if (MainContent.Content != _mainViewContent)
+ {
+ MainContent.Content = _mainViewContent;
+ }
+
AppHost = null;
HandleRelaunch();
diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
index 5b2ea276..73ac0624 100644
--- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml.cs
@@ -211,9 +211,9 @@ namespace Ryujinx.Ava.Ui.Windows
}
}
- private async void SaveButton_Clicked(object sender, RoutedEventArgs e)
+ private void SaveButton_Clicked(object sender, RoutedEventArgs e)
{
- await SaveSettings();
+ SaveSettings();
Close();
}
@@ -224,14 +224,14 @@ namespace Ryujinx.Ava.Ui.Windows
Close();
}
- private async void ApplyButton_Clicked(object sender, RoutedEventArgs e)
+ private void ApplyButton_Clicked(object sender, RoutedEventArgs e)
{
- await SaveSettings();
+ SaveSettings();
}
- private async Task SaveSettings()
+ private void SaveSettings()
{
- await ViewModel.SaveSettings();
+ ViewModel.SaveSettings();
ControllerSettings?.SaveCurrentProfile();
diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/Ryujinx.Graphics.GAL/IWindow.cs
index 043193c9..a9bbbc5e 100644
--- a/Ryujinx.Graphics.GAL/IWindow.cs
+++ b/Ryujinx.Graphics.GAL/IWindow.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.GAL
{
public interface IWindow
{
- void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback);
+ void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
void SetSize(int width, int height);
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs
index f0fec173..c4f3b553 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/Window/WindowPresentCommand.cs
@@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Window
public CommandType CommandType => CommandType.WindowPresent;
private TableRef<ThreadedTexture> _texture;
private ImageCrop _crop;
- private TableRef<Action<object>> _swapBuffersCallback;
+ private TableRef<Action> _swapBuffersCallback;
- public void Set(TableRef<ThreadedTexture> texture, ImageCrop crop, TableRef<Action<object>> swapBuffersCallback)
+ public void Set(TableRef<ThreadedTexture> texture, ImageCrop crop, TableRef<Action> swapBuffersCallback)
{
_texture = texture;
_crop = crop;
diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs
index 21a66e7c..c4b62a25 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs
@@ -16,13 +16,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_impl = impl;
}
- public void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback)
+ public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{
// If there's already a frame in the pipeline, wait for it to be presented first.
// This is a multithread rate limit - we can't be more than one frame behind the command queue.
_renderer.WaitForFrame();
- _renderer.New<WindowPresentCommand>().Set(new TableRef<ThreadedTexture>(_renderer, texture as ThreadedTexture), crop, new TableRef<Action<object>>(_renderer, swapBuffersCallback));
+ _renderer.New<WindowPresentCommand>().Set(new TableRef<ThreadedTexture>(_renderer, texture as ThreadedTexture), crop, new TableRef<Action>(_renderer, swapBuffersCallback));
_renderer.QueueCommand();
}
diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs
index 922583cf..8ad70c7f 100644
--- a/Ryujinx.Graphics.Gpu/Window.cs
+++ b/Ryujinx.Graphics.Gpu/Window.cs
@@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.Gpu
/// If the queue is empty, then no texture is presented.
/// </summary>
/// <param name="swapBuffersCallback">Callback method to call when a new texture should be presented on the screen</param>
- public void Present(Action<object> swapBuffersCallback)
+ public void Present(Action swapBuffersCallback)
{
_context.AdvanceSequence();
diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs
index edebf1a0..61b739b1 100644
--- a/Ryujinx.Graphics.OpenGL/Window.cs
+++ b/Ryujinx.Graphics.OpenGL/Window.cs
@@ -12,11 +12,7 @@ namespace Ryujinx.Graphics.OpenGL
private int _width;
private int _height;
- private bool _sizeChanged;
private int _copyFramebufferHandle;
- private int _stagingFrameBuffer;
- private int[] _stagingTextures;
- private int _currentTexture;
internal BackgroundContextWorker BackgroundContext { get; private set; }
@@ -25,28 +21,15 @@ namespace Ryujinx.Graphics.OpenGL
public Window(OpenGLRenderer renderer)
{
_renderer = renderer;
- _stagingTextures = new int[TextureCount];
}
- public void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback)
+ public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{
GL.Disable(EnableCap.FramebufferSrgb);
- if (_sizeChanged)
- {
- if (_stagingFrameBuffer != 0)
- {
- GL.DeleteTextures(_stagingTextures.Length, _stagingTextures);
- GL.DeleteFramebuffer(_stagingFrameBuffer);
- }
-
- CreateStagingFramebuffer();
- _sizeChanged = false;
- }
-
(int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
- CopyTextureToFrameBufferRGB(_stagingFrameBuffer, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
+ CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
@@ -59,41 +42,17 @@ namespace Ryujinx.Graphics.OpenGL
public void ChangeVSyncMode(bool vsyncEnabled) { }
- private void CreateStagingFramebuffer()
- {
- _stagingFrameBuffer = GL.GenFramebuffer();
- GL.GenTextures(_stagingTextures.Length, _stagingTextures);
-
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, _stagingFrameBuffer);
-
- foreach (var stagingTexture in _stagingTextures)
- {
- GL.BindTexture(TextureTarget.Texture2D, stagingTexture);
- GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, _width, _height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
-
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
- GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, stagingTexture, 0);
- }
-
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
- GL.BindTexture(TextureTarget.Texture2D, 0);
- }
-
public void SetSize(int width, int height)
{
_width = width;
_height = height;
- _sizeChanged = true;
}
- private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action<object> swapBuffersCallback)
+ private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
{
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer);
- GL.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, _stagingTextures[_currentTexture], 0);
-
TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view;
GL.FramebufferTexture(
@@ -189,12 +148,8 @@ namespace Ryujinx.Graphics.OpenGL
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
GL.Viewport(0, 0, _width, _height);
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
- GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _stagingFrameBuffer);
-
- swapBuffersCallback((object)_stagingTextures[_currentTexture]);
- _currentTexture = ++_currentTexture % _stagingTextures.Length;
+ swapBuffersCallback();
((Pipeline)_renderer.Pipeline).RestoreClipControl();
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
@@ -246,14 +201,6 @@ namespace Ryujinx.Graphics.OpenGL
_copyFramebufferHandle = 0;
}
-
- if (_stagingFrameBuffer != 0)
- {
- GL.DeleteTextures(_stagingTextures.Length, _stagingTextures);
- GL.DeleteFramebuffer(_stagingFrameBuffer);
- _stagingFrameBuffer = 0;
- _stagingTextures = null;
- }
}
}
}
diff --git a/Ryujinx.Graphics.Vulkan/ImageWindow.cs b/Ryujinx.Graphics.Vulkan/ImageWindow.cs
deleted file mode 100644
index 69302fdf..00000000
--- a/Ryujinx.Graphics.Vulkan/ImageWindow.cs
+++ /dev/null
@@ -1,429 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Silk.NET.Vulkan;
-using System;
-using VkFormat = Silk.NET.Vulkan.Format;
-
-namespace Ryujinx.Graphics.Vulkan
-{
- class ImageWindow : WindowBase, IWindow, IDisposable
- {
- internal const VkFormat Format = VkFormat.R8G8B8A8Unorm;
-
- private const int ImageCount = 3;
- private const int SurfaceWidth = 1280;
- private const int SurfaceHeight = 720;
-
- private readonly VulkanRenderer _gd;
- private readonly PhysicalDevice _physicalDevice;
- private readonly Device _device;
-
- private Auto<DisposableImage>[] _images;
- private Auto<DisposableImageView>[] _imageViews;
- private Auto<MemoryAllocation>[] _imageAllocationAuto;
- private ImageState[] _states;
- private PresentImageInfo[] _presentedImages;
- private FenceHolder[] _fences;
-
- private ulong[] _imageSizes;
- private ulong[] _imageOffsets;
-
- private int _width = SurfaceWidth;
- private int _height = SurfaceHeight;
- private bool _recreateImages;
- private int _nextImage;
-
- public unsafe ImageWindow(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device)
- {
- _gd = gd;
- _physicalDevice = physicalDevice;
- _device = device;
-
- _images = new Auto<DisposableImage>[ImageCount];
- _imageAllocationAuto = new Auto<MemoryAllocation>[ImageCount];
- _imageSizes = new ulong[ImageCount];
- _imageOffsets = new ulong[ImageCount];
- _states = new ImageState[ImageCount];
- _presentedImages = new PresentImageInfo[ImageCount];
-
- CreateImages();
- }
-
- private void RecreateImages()
- {
- for (int i = 0; i < ImageCount; i++)
- {
- lock (_states[i])
- {
- _states[i].IsValid = false;
- _fences[i]?.Wait();
- _fences[i]?.Put();
- _imageViews[i]?.Dispose();
- _imageAllocationAuto[i]?.Dispose();
- _images[i]?.Dispose();
- }
- }
- _presentedImages = null;
-
- CreateImages();
- }
-
- private unsafe void CreateImages()
- {
- _imageViews = new Auto<DisposableImageView>[ImageCount];
- _fences = new FenceHolder[ImageCount];
- _presentedImages = new PresentImageInfo[ImageCount];
-
- _nextImage = 0;
- var cbs = _gd.CommandBufferPool.Rent();
-
- var imageCreateInfo = new ImageCreateInfo
- {
- SType = StructureType.ImageCreateInfo,
- ImageType = ImageType.ImageType2D,
- Format = Format,
- Extent = new Extent3D((uint?)_width, (uint?)_height, 1),
- MipLevels = 1,
- ArrayLayers = 1,
- Samples = SampleCountFlags.SampleCount1Bit,
- Tiling = ImageTiling.Optimal,
- Usage = ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferSrcBit | ImageUsageFlags.ImageUsageTransferDstBit,
- SharingMode = SharingMode.Exclusive,
- InitialLayout = ImageLayout.Undefined,
- Flags = ImageCreateFlags.ImageCreateMutableFormatBit
- };
-
- for (int i = 0; i < _images.Length; i++)
- {
- _gd.Api.CreateImage(_device, imageCreateInfo, null, out var image).ThrowOnError();
- _images[i] = new Auto<DisposableImage>(new DisposableImage(_gd.Api, _device, image));
-
- _gd.Api.GetImageMemoryRequirements(_device, image,
- out var memoryRequirements);
- var allocation = _gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, memoryRequirements, MemoryPropertyFlags.MemoryPropertyDeviceLocalBit);
-
- _imageSizes[i] = allocation.Size;
- _imageOffsets[i] = allocation.Offset;
-
- _imageAllocationAuto[i] = new Auto<MemoryAllocation>(allocation);
-
- _gd.Api.BindImageMemory(_device, image, allocation.Memory, allocation.Offset);
-
- _imageViews[i] = CreateImageView(image, Format);
-
- Transition(
- cbs.CommandBuffer,
- image,
- 0,
- 0,
- ImageLayout.Undefined,
- ImageLayout.TransferSrcOptimal);
-
- _states[i] = new ImageState();
- }
-
- _gd.CommandBufferPool.Return(cbs);
- }
-
- private unsafe Auto<DisposableImageView> CreateImageView(Image image, VkFormat format)
- {
- var componentMapping = new ComponentMapping(
- ComponentSwizzle.R,
- ComponentSwizzle.G,
- ComponentSwizzle.B,
- ComponentSwizzle.A);
-
- var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
-
- var imageCreateInfo = new ImageViewCreateInfo()
- {
- SType = StructureType.ImageViewCreateInfo,
- Image = image,
- ViewType = ImageViewType.ImageViewType2D,
- Format = format,
- Components = componentMapping,
- SubresourceRange = subresourceRange
- };
-
- _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
- return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
- }
-
- public override unsafe void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback)
- {
- if (_recreateImages)
- {
- RecreateImages();
- _recreateImages = false;
- }
-
- var image = _images[_nextImage];
-
- _gd.FlushAllCommands();
-
- var cbs = _gd.CommandBufferPool.Rent();
-
- Transition(
- cbs.CommandBuffer,
- image.GetUnsafe().Value,
- 0,
- AccessFlags.AccessTransferWriteBit,
- ImageLayout.TransferSrcOptimal,
- ImageLayout.General);
-
- var view = (TextureView)texture;
-
- int srcX0, srcX1, srcY0, srcY1;
- float scale = view.ScaleFactor;
-
- if (crop.Left == 0 && crop.Right == 0)
- {
- srcX0 = 0;
- srcX1 = (int)(view.Width / scale);
- }
- else
- {
- srcX0 = crop.Left;
- srcX1 = crop.Right;
- }
-
- if (crop.Top == 0 && crop.Bottom == 0)
- {
- srcY0 = 0;
- srcY1 = (int)(view.Height / scale);
- }
- else
- {
- srcY0 = crop.Top;
- srcY1 = crop.Bottom;
- }
-
- if (scale != 1f)
- {
- srcX0 = (int)(srcX0 * scale);
- srcY0 = (int)(srcY0 * scale);
- srcX1 = (int)Math.Ceiling(srcX1 * scale);
- srcY1 = (int)Math.Ceiling(srcY1 * scale);
- }
-
- if (ScreenCaptureRequested)
- {
- CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
-
- ScreenCaptureRequested = false;
- }
-
- float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
- float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
-
- int dstWidth = (int)(_width * ratioX);
- int dstHeight = (int)(_height * ratioY);
-
- int dstPaddingX = (_width - dstWidth) / 2;
- int dstPaddingY = (_height - dstHeight) / 2;
-
- int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
- int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
-
- int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
- int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
-
- _gd.HelperShader.Blit(
- _gd,
- cbs,
- view,
- _imageViews[_nextImage],
- _width,
- _height,
- Format,
- new Extents2D(srcX0, srcY0, srcX1, srcY1),
- new Extents2D(dstX0, dstY1, dstX1, dstY0),
- true,
- true);
-
- Transition(
- cbs.CommandBuffer,
- image.GetUnsafe().Value,
- 0,
- 0,
- ImageLayout.General,
- ImageLayout.TransferSrcOptimal);
-
- _gd.CommandBufferPool.Return(
- cbs,
- null,
- stackalloc[] { PipelineStageFlags.PipelineStageColorAttachmentOutputBit },
- null);
-
- _fences[_nextImage]?.Put();
- _fences[_nextImage] = cbs.GetFence();
- cbs.GetFence().Get();
-
- PresentImageInfo info = _presentedImages[_nextImage];
-
- if (info == null)
- {
- info = new PresentImageInfo(
- image,
- _imageAllocationAuto[_nextImage],
- _device,
- _physicalDevice,
- _imageSizes[_nextImage],
- _imageOffsets[_nextImage],
- new Extent2D((uint)_width, (uint)_height),
- _states[_nextImage]);
-
- _presentedImages[_nextImage] = info;
- }
-
- swapBuffersCallback(info);
-
- _nextImage = (_nextImage + 1) % ImageCount;
- }
-
- private unsafe void Transition(
- CommandBuffer commandBuffer,
- Image image,
- AccessFlags srcAccess,
- AccessFlags dstAccess,
- ImageLayout srcLayout,
- ImageLayout dstLayout)
- {
- var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectColorBit, 0, 1, 0, 1);
-
- var barrier = new ImageMemoryBarrier()
- {
- SType = StructureType.ImageMemoryBarrier,
- SrcAccessMask = srcAccess,
- DstAccessMask = dstAccess,
- OldLayout = srcLayout,
- NewLayout = dstLayout,
- SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
- DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
- Image = image,
- SubresourceRange = subresourceRange
- };
-
- _gd.Api.CmdPipelineBarrier(
- commandBuffer,
- PipelineStageFlags.PipelineStageTopOfPipeBit,
- PipelineStageFlags.PipelineStageAllCommandsBit,
- 0,
- 0,
- null,
- 0,
- null,
- 1,
- barrier);
- }
-
- private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
- {
- byte[] bitmap = texture.GetData(x, y, width, height);
-
- _gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
- }
-
- public override void SetSize(int width, int height)
- {
- if (_width != width || _height != height)
- {
- _recreateImages = true;
- }
-
- _width = width;
- _height = height;
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- unsafe
- {
- for (int i = 0; i < ImageCount; i++)
- {
- _states[i].IsValid = false;
- _fences[i]?.Wait();
- _fences[i]?.Put();
- _imageViews[i]?.Dispose();
- _imageAllocationAuto[i]?.Dispose();
- _images[i]?.Dispose();
- }
- }
- }
- }
-
- public override void Dispose()
- {
- Dispose(true);
- }
-
- public override void ChangeVSyncMode(bool vsyncEnabled) { }
- }
-
- public class ImageState
- {
- private bool _isValid = true;
-
- public bool IsValid
- {
- get => _isValid;
- internal set
- {
- _isValid = value;
-
- StateChanged?.Invoke(this, _isValid);
- }
- }
-
- public event EventHandler<bool> StateChanged;
- }
-
- public class PresentImageInfo
- {
- private readonly Auto<DisposableImage> _image;
- private readonly Auto<MemoryAllocation> _memory;
-
- public Image Image => _image.GetUnsafe().Value;
-
- public DeviceMemory Memory => _memory.GetUnsafe().Memory;
-
- public Device Device { get; }
- public PhysicalDevice PhysicalDevice { get; }
- public ulong MemorySize { get; }
- public ulong MemoryOffset { get; }
- public Extent2D Extent { get; }
- public ImageState State { get; internal set; }
- internal PresentImageInfo(
- Auto<DisposableImage> image,
- Auto<MemoryAllocation> memory,
- Device device,
- PhysicalDevice physicalDevice,
- ulong memorySize,
- ulong memoryOffset,
- Extent2D extent2D,
- ImageState state)
- {
- _image = image;
- _memory = memory;
- Device = device;
- PhysicalDevice = physicalDevice;
- MemorySize = memorySize;
- MemoryOffset = memoryOffset;
- Extent = extent2D;
- State = state;
- }
-
- public void Get()
- {
- _memory.IncrementReferenceCount();
- _image.IncrementReferenceCount();
- }
-
- public void Put()
- {
- _memory.DecrementReferenceCount();
- _image.DecrementReferenceCount();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 5abe1be1..3776be9e 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Vulkan
private SurfaceKHR _surface;
private PhysicalDevice _physicalDevice;
private Device _device;
- private uint _queueFamilyIndex;
private WindowBase _window;
internal FormatCapabilities FormatCapabilities { get; private set; }
@@ -37,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan
internal ExtDebugReport DebugReportApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; }
- public bool IsOffScreen { get; }
internal Queue Queue { get; private set; }
internal Queue BackgroundQueue { get; private set; }
internal object BackgroundQueueLock { get; private set; }
@@ -94,22 +92,6 @@ namespace Ryujinx.Graphics.Vulkan
Samplers = new HashSet<SamplerHolder>();
}
- public VulkanRenderer(Instance instance, Device device, PhysicalDevice physicalDevice, Queue queue, uint queueFamilyIndex, object lockObject)
- {
- _instance = instance;
- _physicalDevice = physicalDevice;
- _device = device;
- _queueFamilyIndex = queueFamilyIndex;
-
- Queue = queue;
- QueueLock = lockObject;
-
- IsOffScreen = true;
- Shaders = new HashSet<ShaderCollection>();
- Textures = new HashSet<ITexture>();
- Samplers = new HashSet<SamplerHolder>();
- }
-
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex)
{
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice);
@@ -286,34 +268,6 @@ namespace Ryujinx.Graphics.Vulkan
_window = new Window(this, _surface, _physicalDevice, _device);
}
- private unsafe void SetupOffScreenContext(GraphicsDebugLevel logLevel)
- {
- var api = Vk.GetApi();
-
- Api = api;
-
- VulkanInitialization.CreateDebugCallbacks(api, logLevel, _instance, out var debugReport, out _debugReportCallback);
-
- DebugReportApi = debugReport;
-
- var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice);
-
- uint propertiesCount;
-
- api.GetPhysicalDeviceQueueFamilyProperties(_physicalDevice, &propertiesCount, null);
-
- QueueFamilyProperties[] queueFamilyProperties = new QueueFamilyProperties[propertiesCount];
-
- fixed (QueueFamilyProperties* pProperties = queueFamilyProperties)
- {
- api.GetPhysicalDeviceQueueFamilyProperties(_physicalDevice, &propertiesCount, pProperties);
- }
-
- LoadFeatures(supportedExtensions, queueFamilyProperties[0].QueueCount, _queueFamilyIndex);
-
- _window = new ImageWindow(this, _physicalDevice, _device);
- }
-
public BufferHandle CreateBuffer(int size)
{
return BufferManager.CreateWithHandle(this, size, false);
@@ -519,14 +473,7 @@ namespace Ryujinx.Graphics.Vulkan
public void Initialize(GraphicsDebugLevel logLevel)
{
- if (IsOffScreen)
- {
- SetupOffScreenContext(logLevel);
- }
- else
- {
- SetupContext(logLevel);
- }
+ SetupContext(logLevel);
PrintGpuInformation();
}
@@ -638,15 +585,12 @@ namespace Ryujinx.Graphics.Vulkan
sampler.Dispose();
}
- if (!IsOffScreen)
- {
- SurfaceApi.DestroySurface(_instance, _surface, null);
+ SurfaceApi.DestroySurface(_instance, _surface, null);
- Api.DestroyDevice(_device, null);
+ Api.DestroyDevice(_device, null);
- // Last step destroy the instance
- Api.DestroyInstance(_instance, null);
- }
+ // Last step destroy the instance
+ Api.DestroyInstance(_instance, null);
}
}
}
diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs
index 26f53b39..71b54204 100644
--- a/Ryujinx.Graphics.Vulkan/Window.cs
+++ b/Ryujinx.Graphics.Vulkan/Window.cs
@@ -217,7 +217,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public unsafe override void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback)
+ public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{
uint nextImage = 0;
diff --git a/Ryujinx.Graphics.Vulkan/WindowBase.cs b/Ryujinx.Graphics.Vulkan/WindowBase.cs
index 80b5c0e3..651fe7c1 100644
--- a/Ryujinx.Graphics.Vulkan/WindowBase.cs
+++ b/Ryujinx.Graphics.Vulkan/WindowBase.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
public bool ScreenCaptureRequested { get; set; }
public abstract void Dispose();
- public abstract void Present(ITexture texture, ImageCrop crop, Action<object> swapBuffersCallback);
+ public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
public abstract void SetSize(int width, int height);
public abstract void ChangeVSyncMode(bool vsyncEnabled);
}
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 8ea595e8..4bd1fe39 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -117,7 +117,7 @@ namespace Ryujinx.HLE
return Gpu.Window.ConsumeFrameAvailable();
}
- public void PresentFrame(Action<object> swapBuffersCallback)
+ public void PresentFrame(Action swapBuffersCallback)
{
Gpu.Window.Present(swapBuffersCallback);
}
diff --git a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
index aa2e86d9..d1d0872b 100644
--- a/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
+++ b/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs
@@ -136,7 +136,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL
GL.ClearColor(0, 0, 0, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
- SwapBuffers(0);
+ SwapBuffers();
Renderer?.Window.SetSize(DefaultWidth, DefaultHeight);
MouseDriver.SetClientSize(DefaultWidth, DefaultHeight);
@@ -156,28 +156,8 @@ namespace Ryujinx.Headless.SDL2.OpenGL
_openGLContext.Dispose();
}
- protected override void SwapBuffers(object image)
+ protected override void SwapBuffers()
{
- if ((int)image != 0)
- {
- // The game's framebruffer is already bound, so blit it to the window's backbuffer
- GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
-
- GL.Clear(ClearBufferMask.ColorBufferBit);
- GL.ClearColor(0, 0, 0, 1);
-
- GL.BlitFramebuffer(0,
- 0,
- Width,
- Height,
- 0,
- 0,
- Width,
- Height,
- ClearBufferMask.ColorBufferBit,
- BlitFramebufferFilter.Linear);
- }
-
SDL_GL_SwapWindow(WindowHandle);
}
}
diff --git a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
index 9ec1dc63..0fcf517b 100644
--- a/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
+++ b/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs
@@ -77,6 +77,6 @@ namespace Ryujinx.Headless.SDL2.Vulkan
Device.DisposeGpu();
}
- protected override void SwapBuffers(object texture) { }
+ protected override void SwapBuffers() { }
}
}
diff --git a/Ryujinx.Headless.SDL2/WindowBase.cs b/Ryujinx.Headless.SDL2/WindowBase.cs
index cc0986a0..2d6963dd 100644
--- a/Ryujinx.Headless.SDL2/WindowBase.cs
+++ b/Ryujinx.Headless.SDL2/WindowBase.cs
@@ -157,7 +157,7 @@ namespace Ryujinx.Headless.SDL2
protected abstract void FinalizeWindowRenderer();
- protected abstract void SwapBuffers(object image);
+ protected abstract void SwapBuffers();
public abstract SDL_WindowFlags GetWindowFlags();
@@ -202,7 +202,7 @@ namespace Ryujinx.Headless.SDL2
while (Device.ConsumeFrameAvailable())
{
- Device.PresentFrame((texture) => { SwapBuffers(texture); });
+ Device.PresentFrame(SwapBuffers);
}
if (_ticks >= _ticksPerFrame)
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index b839e9cd..06d414ed 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -97,31 +97,11 @@ namespace Ryujinx.Ui
GL.ClearColor(0, 0, 0, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
- SwapBuffers(0);
+ SwapBuffers();
}
- public override void SwapBuffers(object image)
+ public override void SwapBuffers()
{
- if((int)image != 0)
- {
- // The game's framebruffer is already bound, so blit it to the window's backbuffer
- GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
-
- GL.Clear(ClearBufferMask.ColorBufferBit);
- GL.ClearColor(0, 0, 0, 1);
-
- GL.BlitFramebuffer(0,
- 0,
- WindowWidth,
- WindowHeight,
- 0,
- 0,
- WindowWidth,
- WindowHeight,
- ClearBufferMask.ColorBufferBit,
- BlitFramebufferFilter.Linear);
- }
-
_nativeWindow.SwapBuffers();
}
diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs
index 3cdc424e..6a728a26 100644
--- a/Ryujinx/Ui/RendererWidgetBase.cs
+++ b/Ryujinx/Ui/RendererWidgetBase.cs
@@ -119,7 +119,7 @@ namespace Ryujinx.Ui
public abstract void InitializeRenderer();
- public abstract void SwapBuffers(object image);
+ public abstract void SwapBuffers();
protected abstract string GetGpuBackendName();
@@ -426,7 +426,7 @@ namespace Ryujinx.Ui
while (Device.ConsumeFrameAvailable())
{
- Device.PresentFrame((texture) => { SwapBuffers(texture);});
+ Device.PresentFrame(SwapBuffers);
}
if (_ticks >= _ticksPerFrame)
diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs
index f65b330b..49578d2a 100644
--- a/Ryujinx/Ui/VKRenderer.cs
+++ b/Ryujinx/Ui/VKRenderer.cs
@@ -63,7 +63,7 @@ namespace Ryujinx.Ui
public override void InitializeRenderer() { }
- public override void SwapBuffers(object image) { }
+ public override void SwapBuffers() { }
protected override string GetGpuBackendName()
{