From 784cf9d5947d60d146e518a7913220155362396b Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Mon, 16 Jan 2023 01:14:01 +0100
Subject: [PATCH] Ava UI: `Renderer` refactoring (#4297)

* Ava UI: `Renderer` refactoring

* Fix Vulkan CreateSurface
---
 Ryujinx.Ava/AppHost.cs                        |  46 ++--
 .../Applet/AvaloniaDynamicTextInputHandler.cs |   2 +-
 Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs | 127 ---------
 Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs  |  16 --
 Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs  |  16 --
 Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs      | 235 ----------------
 Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs |  25 --
 .../UI/Helpers/VulkanEmbeddedWindow.cs        |  52 ----
 Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs     | 258 ++++++++++++++++++
 .../EmbeddedWindowOpenGL.cs}                  |  37 +--
 .../UI/Renderer/EmbeddedWindowVulkan.cs       |  42 +++
 .../OpenTKBindingsContext.cs}                 |   6 +-
 .../{Controls => Renderer}/RendererHost.axaml |   4 +-
 Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs |  69 +++++
 .../{Helpers => Renderer}/SPBOpenGLContext.cs |  12 +-
 .../UI/ViewModels/MainWindowViewModel.cs      |  27 +-
 16 files changed, 434 insertions(+), 540 deletions(-)
 delete mode 100644 Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs
 delete mode 100644 Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs
 delete mode 100644 Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs
 delete mode 100644 Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs
 delete mode 100644 Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs
 delete mode 100644 Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs
 create mode 100644 Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
 rename Ryujinx.Ava/UI/{Helpers/OpenGLEmbeddedWindow.cs => Renderer/EmbeddedWindowOpenGL.cs} (60%)
 create mode 100644 Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs
 rename Ryujinx.Ava/UI/{Helpers/OpenToolkitBindingsContext.cs => Renderer/OpenTKBindingsContext.cs} (62%)
 rename Ryujinx.Ava/UI/{Controls => Renderer}/RendererHost.axaml (84%)
 create mode 100644 Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
 rename Ryujinx.Ava/UI/{Helpers => Renderer}/SPBOpenGLContext.cs (73%)

diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs
index 5c4f5bd8b2..067be5c018 100644
--- a/Ryujinx.Ava/AppHost.cs
+++ b/Ryujinx.Ava/AppHost.cs
@@ -12,9 +12,9 @@ using Ryujinx.Audio.Integration;
 using Ryujinx.Ava.Common;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.Input;
-using Ryujinx.Ava.UI.Controls;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Models;
+using Ryujinx.Ava.UI.Renderer;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Common;
@@ -44,7 +44,6 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
-using System.Runtime.Versioning;
 using System.Threading;
 using System.Threading.Tasks;
 using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
@@ -66,8 +65,8 @@ namespace Ryujinx.Ava
         private const float VolumeDelta        = 0.05f;
 
         private static readonly Cursor InvisibleCursor = new(StandardCursorType.None);
-        private readonly IntPtr InvisibleCursorWin;
-        private readonly IntPtr DefaultCursorWin;
+        private readonly IntPtr        InvisibleCursorWin;
+        private readonly IntPtr        DefaultCursorWin;
 
         private readonly long      _ticksPerFrame;
         private readonly Stopwatch _chrono;
@@ -80,6 +79,7 @@ namespace Ryujinx.Ava
         private readonly MainWindowViewModel _viewModel;
         private readonly IKeyboard           _keyboardInterface;
         private readonly TopLevel            _topLevel;
+        public           RendererHost        _rendererHost;
 
         private readonly GraphicsDebugLevel _glLogLevel;
         private float                       _newVolume;
@@ -105,7 +105,6 @@ namespace Ryujinx.Ava
         public event EventHandler AppExit;
         public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
 
-        public RendererHost       Renderer           { get; }
         public VirtualFileSystem  VirtualFileSystem  { get; }
         public ContentManager     ContentManager     { get; }
         public NpadManager        NpadManager        { get; }
@@ -117,7 +116,6 @@ namespace Ryujinx.Ava
         public string ApplicationPath     { get; private set; }
         public bool   ScreenshotRequested { get; set; }
 
-
         public AppHost(
             RendererHost           renderer,
             InputManager           inputManager,
@@ -144,11 +142,12 @@ namespace Ryujinx.Ava
 
             NpadManager        = _inputManager.CreateNpadManager();
             TouchScreenManager = _inputManager.CreateTouchScreenManager();
-            Renderer           = renderer;
             ApplicationPath    = applicationPath;
             VirtualFileSystem  = virtualFileSystem;
             ContentManager     = contentManager;
 
+            _rendererHost = renderer;
+
             _chrono        = new Stopwatch();
             _ticksPerFrame = Stopwatch.Frequency / TargetFps;
 
@@ -183,10 +182,10 @@ namespace Ryujinx.Ava
             {
                 _lastCursorMoveTime = Stopwatch.GetTimestamp();
 
-                if ((Renderer.Content as EmbeddedWindow).TransformedBounds != null)
+                if (_rendererHost.EmbeddedWindow.TransformedBounds != null)
                 {
                     var point  = e.GetCurrentPoint(window).Position;
-                    var bounds = (Renderer.Content as EmbeddedWindow).TransformedBounds.Value.Clip;
+                    var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
 
                     _isCursorInRenderer = point.X >= bounds.X &&
                                           point.X <= bounds.Width + bounds.X &&
@@ -318,7 +317,7 @@ namespace Ryujinx.Ava
 
             _viewModel.SetUIProgressHandlers(Device);
 
-            Renderer.SizeChanged += Window_SizeChanged;
+            _rendererHost.SizeChanged += Window_SizeChanged;
 
             _isActive = true;
 
@@ -430,11 +429,11 @@ namespace Ryujinx.Ava
                 _windowsMultimediaTimerResolution = null;
             }
 
-            Renderer?.MakeCurrent();
+            (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent();
 
             Device.DisposeGpu();
 
-            Renderer?.MakeCurrent(null);
+            (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
         }
 
         private void HideCursorState_Changed(object sender, ReactiveEventArgs<bool> state)
@@ -635,11 +634,12 @@ namespace Ryujinx.Ava
             // Initialize Renderer.
             IRenderer renderer;
 
-            if (Renderer.IsVulkan)
+            if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
             {
-                string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
-
-                renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
+                renderer = new VulkanRenderer(
+                    (_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
+                    VulkanHelper.GetRequiredInstanceExtensions,
+                    ConfigurationState.Instance.Graphics.PreferredGpu.Value);
             }
             else
             {
@@ -787,14 +787,12 @@ namespace Ryujinx.Ava
 
             _renderer.ScreenCaptured += Renderer_ScreenCaptured;
 
-            (_renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Renderer.GetContext()));
-
-            Renderer.MakeCurrent();
+            (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
 
             Device.Gpu.Renderer.Initialize(_glLogLevel);
 
-            Width = (int)Renderer.Bounds.Width;
-            Height = (int)Renderer.Bounds.Height;
+            Width = (int)_rendererHost.Bounds.Width;
+            Height = (int)_rendererHost.Bounds.Height;
 
             _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
 
@@ -827,7 +825,7 @@ namespace Ryujinx.Ava
                             _viewModel.SwitchToRenderer(false);
                         }
 
-                        Device.PresentFrame(() => Renderer?.SwapBuffers());
+                        Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
                     }
 
                     if (_ticks >= _ticksPerFrame)
@@ -837,7 +835,7 @@ namespace Ryujinx.Ava
                 }
             });
 
-            Renderer?.MakeCurrent(null);
+            (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
         }
 
         public void UpdateStatus()
@@ -853,7 +851,7 @@ namespace Ryujinx.Ava
             StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
                 Device.EnableDeviceVsync,
                 LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
-                Renderer.IsVulkan ? "Vulkan" : "OpenGL",
+                ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan ? "Vulkan" : "OpenGL",
                 dockedMode,
                 ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
                 LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
diff --git a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs
index bae4762ebe..2dd65e3629 100644
--- a/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs
+++ b/Ryujinx.Ava/UI/Applet/AvaloniaDynamicTextInputHandler.cs
@@ -136,7 +136,7 @@ namespace Ryujinx.Ava.UI.Applet
             Dispatcher.UIThread.Post(() =>
             {
                 _hiddenTextBox.Clear();
-                _parent.ViewModel.RendererControl.Focus();
+                _parent.ViewModel.RendererHostControl.Focus();
 
                 _parent = null;
             });
diff --git a/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs
deleted file mode 100644
index 97058fa499..0000000000
--- a/Ryujinx.Ava/UI/Controls/RendererHost.axaml.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using Ryujinx.Ava.UI.Helpers;
-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;
-            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/Helpers/AvaloniaGlxContext.cs b/Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs
deleted file mode 100644
index 6b696ba739..0000000000
--- a/Ryujinx.Ava/UI/Helpers/AvaloniaGlxContext.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using SPB.Graphics;
-using System;
-using System.Runtime.Versioning;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    [SupportedOSPlatform("linux")]
-    internal class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext
-    {
-        public AvaloniaGlxContext(IntPtr handle)
-            : base(FramebufferFormat.Default, 0, 0, 0, false, null)
-        {
-            ContextHandle = handle;
-        }
-    }
-}
diff --git a/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs b/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs
deleted file mode 100644
index b63a973a11..0000000000
--- a/Ryujinx.Ava/UI/Helpers/AvaloniaWglContext.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using SPB.Graphics;
-using System;
-using System.Runtime.Versioning;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    [SupportedOSPlatform("windows")]
-    internal class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext
-    {
-        public AvaloniaWglContext(IntPtr handle)
-            : base(FramebufferFormat.Default, 0, 0, 0, false, null)
-        {
-            ContextHandle = handle;
-        }
-    }
-}
diff --git a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs
deleted file mode 100644
index 67ab80aa70..0000000000
--- a/Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Input;
-using Avalonia.Platform;
-using SPB.Graphics;
-using SPB.Platform;
-using SPB.Platform.GLX;
-using System;
-using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
-using System.Threading.Tasks;
-using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    public class EmbeddedWindow : NativeControlHost
-    {
-        private WindowProc _wndProcDelegate;
-        private string _className;
-
-        protected GLXWindow X11Window { get; set; }
-        protected IntPtr WindowHandle { get; set; }
-        protected IntPtr X11Display { get; set; }
-        protected IntPtr NsView { get; set; }
-        protected IntPtr MetalLayer { get; set; }
-
-        private UpdateBoundsCallbackDelegate _updateBoundsCallback;
-
-        public event EventHandler<IntPtr> WindowCreated;
-        public event EventHandler<Size> SizeChanged;
-
-        protected virtual void OnWindowDestroyed() { }
-        protected virtual void OnWindowDestroying()
-        {
-            WindowHandle = IntPtr.Zero;
-            X11Display = IntPtr.Zero;
-            NsView = IntPtr.Zero;
-            MetalLayer = IntPtr.Zero;
-        }
-
-        public EmbeddedWindow()
-        {
-            var stateObserverable = this.GetObservable(BoundsProperty);
-
-            stateObserverable.Subscribe(StateChanged);
-
-            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);
-            _updateBoundsCallback?.Invoke(rect);
-        }
-
-        protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
-        {
-            if (OperatingSystem.IsLinux())
-            {
-                return CreateLinux(parent);
-            }
-            else if (OperatingSystem.IsWindows())
-            {
-                return CreateWin32(parent);
-            }
-            else if (OperatingSystem.IsMacOS())
-            {
-                return CreateMacOs(parent);
-            }
-
-            return base.CreateNativeControlCore(parent);
-        }
-
-        protected override void DestroyNativeControlCore(IPlatformHandle control)
-        {
-            OnWindowDestroying();
-
-            if (OperatingSystem.IsLinux())
-            {
-                DestroyLinux();
-            }
-            else if (OperatingSystem.IsWindows())
-            {
-                DestroyWin32(control);
-            }
-            else if (OperatingSystem.IsMacOS())
-            {
-                DestroyMacOS();
-            }
-            else
-            {
-                base.DestroyNativeControlCore(control);
-            }
-
-            OnWindowDestroyed();
-        }
-
-        [SupportedOSPlatform("linux")]
-        protected virtual 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")]
-        IPlatformHandle CreateWin32(IPlatformHandle parent)
-        {
-            _className = "NativeWindow-" + Guid.NewGuid();
-            _wndProcDelegate = WndProc;
-            var wndClassEx = new WNDCLASSEX
-            {
-                cbSize = Marshal.SizeOf<WNDCLASSEX>(),
-                hInstance = GetModuleHandle(null),
-                lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
-                style = ClassStyles.CS_OWNDC,
-                lpszClassName = Marshal.StringToHGlobalUni(_className),
-                hCursor = CreateArrowCursor()
-            };
-
-            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;
-
-            Marshal.FreeHGlobal(wndClassEx.lpszClassName);
-
-            return new PlatformHandle(WindowHandle, "HWND");
-        }
-
-        [SupportedOSPlatform("windows")]
-        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 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 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 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, wParam, lParam);
-        }
-
-        [SupportedOSPlatform("macos")]
-        IPlatformHandle CreateMacOs(IPlatformHandle parent)
-        {
-            MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
-
-            NsView = nsView;
-
-            return new PlatformHandle(nsView, "NSView");
-        }
-
-        void DestroyLinux()
-        {
-            X11Window?.Dispose();
-        }
-
-        [SupportedOSPlatform("windows")]
-        void DestroyWin32(IPlatformHandle handle)
-        {
-            DestroyWindow(handle.Handle);
-            UnregisterClass(_className, GetModuleHandle(null));
-        }
-
-        [SupportedOSPlatform("macos")]
-        void DestroyMacOS()
-        {
-            MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs b/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs
deleted file mode 100644
index e69774c307..0000000000
--- a/Ryujinx.Ava/UI/Helpers/IGlContextExtension.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Avalonia.OpenGL;
-using SPB.Graphics.OpenGL;
-using System;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    internal static class IGlContextExtension
-    {
-        public static OpenGLContextBase AsOpenGLContextBase(this IGlContext context)
-        {
-            var handle = (IntPtr)context.GetType().GetProperty("Handle").GetValue(context);
-
-            if (OperatingSystem.IsWindows())
-            {
-                return new AvaloniaWglContext(handle);
-            }
-            else if (OperatingSystem.IsLinux())
-            {
-                return new AvaloniaGlxContext(handle);
-            }
-
-            return null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs b/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs
deleted file mode 100644
index 6581610b6a..0000000000
--- a/Ryujinx.Ava/UI/Helpers/VulkanEmbeddedWindow.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Avalonia.Platform;
-using Silk.NET.Vulkan;
-using SPB.Graphics.Vulkan;
-using SPB.Platform.GLX;
-using SPB.Platform.Metal;
-using SPB.Platform.Win32;
-using SPB.Platform.X11;
-using SPB.Windowing;
-using System;
-using System.Runtime.Versioning;
-
-namespace Ryujinx.Ava.UI.Helpers
-{
-    public class VulkanEmbeddedWindow : EmbeddedWindow
-    {
-        private NativeWindowBase _window;
-
-        [SupportedOSPlatform("linux")]
-        protected override IPlatformHandle CreateLinux(IPlatformHandle parent)
-        {
-            X11Window    = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle));
-            WindowHandle = X11Window.WindowHandle.RawHandle;
-            X11Display   = X11Window.DisplayHandle.RawHandle;
-
-            X11Window.Hide();
-
-            return new PlatformHandle(WindowHandle, "X11");
-        }
-
-        public SurfaceKHR CreateSurface(Instance instance)
-        {
-            if (OperatingSystem.IsWindows())
-            {
-                _window = new SimpleWin32Window(new NativeHandle(WindowHandle));
-            }
-            else if (OperatingSystem.IsLinux())
-            {
-                _window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle));
-            }
-            else if (OperatingSystem.IsMacOS())
-            {
-                _window = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer));
-            }
-            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/Renderer/EmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
new file mode 100644
index 0000000000..21d9d12e2c
--- /dev/null
+++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
@@ -0,0 +1,258 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Platform;
+using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Ui.Common.Configuration;
+using SPB.Graphics;
+using SPB.Platform;
+using SPB.Platform.GLX;
+using SPB.Platform.X11;
+using SPB.Windowing;
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Threading.Tasks;
+using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+    public class EmbeddedWindow : NativeControlHost
+    {
+        private WindowProc _wndProcDelegate;
+        private string     _className;
+
+        protected GLXWindow X11Window { get; set; }
+
+        protected IntPtr WindowHandle { get; set; }
+        protected IntPtr X11Display   { get; set; }
+        protected IntPtr NsView       { get; set; }
+        protected IntPtr MetalLayer   { get; set; }
+
+        private UpdateBoundsCallbackDelegate _updateBoundsCallback;
+
+        public event EventHandler<IntPtr> WindowCreated;
+        public event EventHandler<Size>   SizeChanged;
+
+        public EmbeddedWindow()
+        {
+            this.GetObservable(BoundsProperty).Subscribe(StateChanged);
+
+            Initialized += OnNativeEmbeddedWindowCreated;
+        }
+
+        public virtual void OnWindowCreated() { }
+
+        protected virtual void OnWindowDestroyed() { }
+
+        protected virtual void OnWindowDestroying()
+        {
+            WindowHandle = IntPtr.Zero;
+            X11Display   = IntPtr.Zero;
+            NsView       = IntPtr.Zero;
+            MetalLayer   = IntPtr.Zero;
+        }
+
+        private void OnNativeEmbeddedWindowCreated(object sender, EventArgs e)
+        {
+            OnWindowCreated();
+
+            Task.Run(() =>
+            {
+                WindowCreated?.Invoke(this, WindowHandle);
+            });
+        }
+
+        private void StateChanged(Rect rect)
+        {
+            SizeChanged?.Invoke(this, rect.Size);
+            _updateBoundsCallback?.Invoke(rect);
+        }
+
+        protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle control)
+        {
+            if (OperatingSystem.IsLinux())
+            {
+                return CreateLinux(control);
+            }
+            else if (OperatingSystem.IsWindows())
+            {
+                return CreateWin32(control);
+            }
+            else if (OperatingSystem.IsMacOS())
+            {
+                return CreateMacOs();
+            }
+
+            return base.CreateNativeControlCore(control);
+        }
+
+        protected override void DestroyNativeControlCore(IPlatformHandle control)
+        {
+            OnWindowDestroying();
+
+            if (OperatingSystem.IsLinux())
+            {
+                DestroyLinux();
+            }
+            else if (OperatingSystem.IsWindows())
+            {
+                DestroyWin32(control);
+            }
+            else if (OperatingSystem.IsMacOS())
+            {
+                DestroyMacOS();
+            }
+            else
+            {
+                base.DestroyNativeControlCore(control);
+            }
+
+            OnWindowDestroyed();
+        }
+
+        [SupportedOSPlatform("linux")]
+        protected virtual IPlatformHandle CreateLinux(IPlatformHandle control)
+        {
+            if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
+            {
+                X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
+            }
+            else
+            {
+                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")]
+        IPlatformHandle CreateWin32(IPlatformHandle control)
+        {
+            _className = "NativeWindow-" + Guid.NewGuid();
+
+            _wndProcDelegate = delegate (IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
+            {
+                if (VisualRoot != null)
+                {
+                    Point   rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
+                    Pointer pointer            = new(0, PointerType.Mouse, true);
+
+                    switch (msg)
+                    {
+                        case WindowsMessages.LBUTTONDOWN:
+                        case WindowsMessages.RBUTTONDOWN:
+                            {
+                                bool                   isLeft               = msg == WindowsMessages.LBUTTONDOWN;
+                                RawInputModifiers      pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
+                                PointerPointProperties properties           = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
+
+                                var evnt = new PointerPressedEventArgs(
+                                    this,
+                                    pointer,
+                                    VisualRoot,
+                                    rootVisualPosition,
+                                    (ulong)Environment.TickCount64,
+                                    properties,
+                                    KeyModifiers.None);
+
+                                RaiseEvent(evnt);
+
+                                break;
+                            }
+                        case WindowsMessages.LBUTTONUP:
+                        case WindowsMessages.RBUTTONUP:
+                            {
+                                bool                   isLeft               = msg == WindowsMessages.LBUTTONUP;
+                                RawInputModifiers      pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
+                                PointerPointProperties properties           = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
+
+                                var evnt = new PointerReleasedEventArgs(
+                                    this,
+                                    pointer,
+                                    VisualRoot,
+                                    rootVisualPosition,
+                                    (ulong)Environment.TickCount64,
+                                    properties,
+                                    KeyModifiers.None,
+                                    isLeft ? MouseButton.Left : MouseButton.Right);
+
+                                RaiseEvent(evnt);
+
+                                break;
+                            }
+                        case WindowsMessages.MOUSEMOVE:
+                            {
+                                var evnt = new PointerEventArgs(
+                                    PointerMovedEvent,
+                                    this,
+                                    pointer,
+                                    VisualRoot,
+                                    rootVisualPosition,
+                                    (ulong)Environment.TickCount64,
+                                    new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.Other),
+                                    KeyModifiers.None);
+
+                                RaiseEvent(evnt);
+
+                                break;
+                            }
+                    }
+                }
+
+                return DefWindowProc(hWnd, msg, wParam, lParam);
+            };
+
+            WNDCLASSEX wndClassEx = new()
+            {
+                cbSize        = Marshal.SizeOf<WNDCLASSEX>(),
+                hInstance     = GetModuleHandle(null),
+                lpfnWndProc   = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
+                style         = ClassStyles.CS_OWNDC,
+                lpszClassName = Marshal.StringToHGlobalUni(_className),
+                hCursor       = CreateArrowCursor()
+            };
+
+            RegisterClassEx(ref wndClassEx);
+
+            WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+
+            Marshal.FreeHGlobal(wndClassEx.lpszClassName);
+
+            return new PlatformHandle(WindowHandle, "HWND");
+        }
+
+        [SupportedOSPlatform("macos")]
+        IPlatformHandle CreateMacOs()
+        {
+            MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
+
+            NsView = nsView;
+
+            return new PlatformHandle(nsView, "NSView");
+        }
+
+        [SupportedOSPlatform("Linux")]
+        void DestroyLinux()
+        {
+            X11Window?.Dispose();
+        }
+
+        [SupportedOSPlatform("windows")]
+        void DestroyWin32(IPlatformHandle handle)
+        {
+            DestroyWindow(handle.Handle);
+            UnregisterClass(_className, GetModuleHandle(null));
+        }
+
+        [SupportedOSPlatform("macos")]
+        void DestroyMacOS()
+        {
+            MetalHelper.DestroyMetalLayer(NsView, MetalLayer);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs
similarity index 60%
rename from Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs
rename to Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs
index db77f66bf0..305e891a1e 100644
--- a/Ryujinx.Ava/UI/Helpers/OpenGLEmbeddedWindow.cs
+++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs
@@ -1,5 +1,8 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Configuration;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL;
+using Ryujinx.Ui.Common.Configuration;
 using SPB.Graphics;
 using SPB.Graphics.OpenGL;
 using SPB.Platform;
@@ -7,26 +10,20 @@ using SPB.Platform.WGL;
 using SPB.Windowing;
 using System;
 
-namespace Ryujinx.Ava.UI.Helpers
+namespace Ryujinx.Ava.UI.Renderer
 {
-    public class OpenGLEmbeddedWindow : EmbeddedWindow
+    public class EmbeddedWindowOpenGL : 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;
-        }
+        public EmbeddedWindowOpenGL() { }
 
         protected override void OnWindowDestroying()
         {
             Context.Dispose();
+
             base.OnWindowDestroying();
         }
 
@@ -48,19 +45,20 @@ namespace Ryujinx.Ava.UI.Helpers
             }
 
             var flags = OpenGLContextFlags.Compat;
-            if (_graphicsDebugLevel != GraphicsDebugLevel.None)
+            if (ConfigurationState.Instance.Logger.GraphicsDebugLevel != GraphicsDebugLevel.None)
             {
                 flags |= OpenGLContextFlags.Debug;
             }
 
-            Context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, _major, _minor, flags);
+            var graphicsMode = Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default;
+
+            Context = PlatformHelper.CreateOpenGLContext(graphicsMode, 3, 3, flags);
 
             Context.Initialize(_window);
             Context.MakeCurrent(_window);
 
-            var bindingsContext = new OpenToolkitBindingsContext(Context.GetProcAddress);
+            GL.LoadBindings(new OpenTKBindingsContext(Context.GetProcAddress));
 
-            GL.LoadBindings(bindingsContext);
             Context.MakeCurrent(null);
         }
 
@@ -76,7 +74,14 @@ namespace Ryujinx.Ava.UI.Helpers
 
         public void SwapBuffers()
         {
-            _window.SwapBuffers();
+            _window?.SwapBuffers();
+        }
+
+        public void InitializeBackgroundContext(IRenderer renderer)
+        {
+            (renderer as OpenGLRenderer)?.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(Context));
+
+            MakeCurrent();
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs
new file mode 100644
index 0000000000..0b3eb9e30c
--- /dev/null
+++ b/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs
@@ -0,0 +1,42 @@
+using Silk.NET.Vulkan;
+using SPB.Graphics.Vulkan;
+using SPB.Platform.Metal;
+using SPB.Platform.Win32;
+using SPB.Platform.X11;
+using SPB.Windowing;
+using System;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+    public class EmbeddedWindowVulkan : EmbeddedWindow
+    {
+        public SurfaceKHR CreateSurface(Instance instance)
+        {
+            NativeWindowBase nativeWindowBase;
+
+            if (OperatingSystem.IsWindows())
+            {
+                nativeWindowBase = new SimpleWin32Window(new NativeHandle(WindowHandle));
+            }
+            else if (OperatingSystem.IsLinux())
+            {
+                nativeWindowBase = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle));
+            }
+            else if (OperatingSystem.IsMacOS())
+            {
+                nativeWindowBase = new SimpleMetalWindow(new NativeHandle(NsView), new NativeHandle(MetalLayer));
+            }
+            else
+            {
+                throw new PlatformNotSupportedException();
+            }
+
+            return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase));
+        }
+
+        public SurfaceKHR CreateSurface(Instance instance, Vk api)
+        {
+            return CreateSurface(instance);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs b/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs
similarity index 62%
rename from Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs
rename to Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs
index efb703bab6..a2ec02b253 100644
--- a/Ryujinx.Ava/UI/Helpers/OpenToolkitBindingsContext.cs
+++ b/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs
@@ -1,13 +1,13 @@
 using OpenTK;
 using System;
 
-namespace Ryujinx.Ava.UI.Helpers
+namespace Ryujinx.Ava.UI.Renderer
 {
-    internal class OpenToolkitBindingsContext : IBindingsContext
+    internal class OpenTKBindingsContext : IBindingsContext
     {
         private readonly Func<string, IntPtr> _getProcAddress;
 
-        public OpenToolkitBindingsContext(Func<string, IntPtr> getProcAddress)
+        public OpenTKBindingsContext(Func<string, IntPtr> getProcAddress)
         {
             _getProcAddress = getProcAddress;
         }
diff --git a/Ryujinx.Ava/UI/Controls/RendererHost.axaml b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml
similarity index 84%
rename from Ryujinx.Ava/UI/Controls/RendererHost.axaml
rename to Ryujinx.Ava/UI/Renderer/RendererHost.axaml
index 1cc557f066..bb96b10d2a 100644
--- a/Ryujinx.Ava/UI/Controls/RendererHost.axaml
+++ b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml
@@ -6,6 +6,6 @@
     mc:Ignorable="d" 
     d:DesignWidth="800" 
     d:DesignHeight="450"
-    x:Class="Ryujinx.Ava.UI.Controls.RendererHost"
+    x:Class="Ryujinx.Ava.UI.Renderer.RendererHost"
     Focusable="True">
-</UserControl>
+</UserControl>
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
new file mode 100644
index 0000000000..16a46df423
--- /dev/null
+++ b/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
@@ -0,0 +1,69 @@
+using Avalonia;
+using Avalonia.Controls;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Ui.Common.Configuration;
+using Silk.NET.Vulkan;
+using System;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+    public partial class RendererHost : UserControl, IDisposable
+    {
+        public EmbeddedWindow EmbeddedWindow;
+
+        public event EventHandler<EventArgs> WindowCreated;
+        public event Action<object, Size>    SizeChanged;
+
+        public RendererHost()
+        {
+            InitializeComponent();
+
+            Dispose();
+
+            if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
+            {
+                EmbeddedWindow = new EmbeddedWindowOpenGL();
+            }
+            else
+            {
+                EmbeddedWindow = new EmbeddedWindowVulkan();
+            }
+
+            Initialize();
+        }
+
+        private void Initialize()
+        {
+            EmbeddedWindow.WindowCreated += CurrentWindow_WindowCreated;
+            EmbeddedWindow.SizeChanged   += CurrentWindow_SizeChanged;
+
+            Content = EmbeddedWindow;
+        }
+
+        public void Dispose()
+        {
+            if (EmbeddedWindow != null)
+            {
+                EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
+                EmbeddedWindow.SizeChanged   -= CurrentWindow_SizeChanged;
+            }
+        }
+
+        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)
+        {
+            WindowCreated?.Invoke(this, EventArgs.Empty);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs b/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs
similarity index 73%
rename from Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs
rename to Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs
index 21f206c83d..e090f14c7a 100644
--- a/Ryujinx.Ava/UI/Helpers/SPBOpenGLContext.cs
+++ b/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs
@@ -5,17 +5,17 @@ using SPB.Graphics.OpenGL;
 using SPB.Platform;
 using SPB.Windowing;
 
-namespace Ryujinx.Ava.UI.Helpers
+namespace Ryujinx.Ava.UI.Renderer
 {
     class SPBOpenGLContext : IOpenGLContext
     {
-        private OpenGLContextBase _context;
-        private NativeWindowBase _window;
+        private readonly OpenGLContextBase _context;
+        private readonly NativeWindowBase  _window;
 
         private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window)
         {
             _context = context;
-            _window = window;
+            _window  = window;
         }
 
         public void Dispose()
@@ -32,12 +32,12 @@ namespace Ryujinx.Ava.UI.Helpers
         public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext)
         {
             OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext);
-            NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
+            NativeWindowBase  window  = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100);
 
             context.Initialize(window);
             context.MakeCurrent(window);
 
-            GL.LoadBindings(new OpenToolkitBindingsContext(context.GetProcAddress));
+            GL.LoadBindings(new OpenTKBindingsContext(context.GetProcAddress));
 
             context.MakeCurrent(null);
 
diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
index 2954021553..a02b642959 100644
--- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
@@ -13,6 +13,7 @@ using Ryujinx.Ava.Input;
 using Ryujinx.Ava.UI.Controls;
 using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Models;
+using Ryujinx.Ava.UI.Renderer;
 using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Common;
 using Ryujinx.Common.Configuration;
@@ -870,7 +871,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         public Action<bool> SwitchToGameControl { get; private set; }
         public Action<Control> SetMainContent { get; private set; }
         public TopLevel TopLevel { get; private set; }
-        public RendererHost RendererControl { get; private set; }
+        public RendererHost RendererHostControl { get; private set; }
         public bool IsClosing { get; set; }
         public LibHacHorizonManager LibHacHorizonManager { get; internal set; }
         public IHostUiHandler UiHandler { get; internal set; }
@@ -1144,7 +1145,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         private void InitializeGame()
         {
-            RendererControl.RendererInitialized += GlRenderer_Created;
+            RendererHostControl.WindowCreated += RendererHost_Created;
 
             AppHost.StatusUpdatedEvent += Update_StatusBar;
             AppHost.AppExit += AppHost_AppExit;
@@ -1203,7 +1204,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
         }
 
-        private void GlRenderer_Created(object sender, EventArgs e)
+        private void RendererHost_Created(object sender, EventArgs e)
         {
             ShowLoading(false);
 
@@ -1731,18 +1732,10 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             PrepareLoadScreen();
 
-            RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel);
-            if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
-            {
-                RendererControl.CreateOpenGL();
-            }
-            else
-            {
-                RendererControl.CreateVulkan();
-            }
+            RendererHostControl = new RendererHost();
 
             AppHost = new AppHost(
-                RendererControl,
+                RendererHostControl,
                 InputManager,
                 path,
                 VirtualFileSystem,
@@ -1783,9 +1776,9 @@ namespace Ryujinx.Ava.UI.ViewModels
             {
                 SwitchToGameControl(startFullscreen);
 
-                SetMainContent(RendererControl);
+                SetMainContent(RendererHostControl);
 
-                RendererControl.Focus();
+                RendererHostControl.Focus();
             });
         }
 
@@ -1853,8 +1846,8 @@ namespace Ryujinx.Ava.UI.ViewModels
                 HandleRelaunch();
             });
 
-            RendererControl.RendererInitialized -= GlRenderer_Created;
-            RendererControl = null;
+            RendererHostControl.WindowCreated -= RendererHost_Created;
+            RendererHostControl = null;
 
             SelectedIcon = null;