diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs
index 7e3cddc887..7cf5934a69 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());
+                    }
+
+                    if (_ticks >= _ticksPerFrame)
+                    {
+                        UpdateStatus();
                     }
                 }
-
-                Renderer.Stop();
             });
 
             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 74c435b5cc..9ad0310a55 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 242246ebee..61b184c61e 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 564ee4b295..e4ddba9661 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 423fe038eb..0000000000
--- 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 617e97678f..0000000000
--- 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 335bc905f5..0000000000
--- 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 2a1cd2293a..0000000000
--- 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 70ec39c7cd..0000000000
--- 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 a5c2708638..0000000000
--- 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 fd2d379b1a..0000000000
--- 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 642d8a6a3b..0000000000
--- 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 510e6724b8..0000000000
--- 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 a00ecf2b97..0000000000
--- 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 3d893e19a2..0000000000
--- 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 f3116fbda2..0000000000
--- 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 3fbb8665e3..0000000000
--- 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 b50e9c07d5..0000000000
--- 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 a705259201..0000000000
--- 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 0027753c8b..0000000000
--- 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 11444d30c0..0000000000
--- 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 ff8d932868..0000000000
--- 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 a903e21a61..0000000000
--- 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 3b5fd9cc6b..0000000000
--- 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 2452cdcd73..0000000000
--- 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 71f5f18ac1..0000000000
--- 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 15ecaa77b0..e774a09a0a 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 0000000000..d9fae93a5b
--- /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 0000000000..ce579fdfde
--- /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 e58bdaa0af..0000000000
--- 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 392f67e35a..0000000000
--- 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 0000000000..be72fd61ee
--- /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 0000000000..0d1984fd5b
--- /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 0000000000..d2c980ddf3
--- /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 7b7dfaa107..0000000000
--- 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 0000000000..124536d991
--- /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 32f08ff93b..10dd2da340 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 0000000000..2967f4f216
--- /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 0000000000..7a51e64d00
--- /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 610281a60a..6eafd5e500 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 6ce1d17d5d..976c28179f 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 5b2ea276c8..73ac062422 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 043193c9cf..a9bbbc5e02 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 f0fec1733b..c4f3b553a0 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 21a66e7cbc..c4b62a25d3 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 922583cf59..8ad70c7f11 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 edebf1a090..61b739b119 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 69302fdf75..0000000000
--- 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 5abe1be153..3776be9eed 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 26f53b3954..71b5420448 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 80b5c0e3f9..651fe7c162 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 8ea595e840..4bd1fe3975 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 aa2e86d9d5..d1d0872b36 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 9ec1dc63bf..0fcf517bea 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 cc0986a0f5..2d6963dd69 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 b839e9cd4c..06d414edca 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 3cdc424ef6..6a728a26d3 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 f65b330b60..49578d2af6 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()
         {