aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Ava/UI/Renderer
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Ava/UI/Renderer
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Ava/UI/Renderer')
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs288
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs87
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs42
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs20
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml11
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs68
-rw-r--r--src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs47
7 files changed, 563 insertions, 0 deletions
diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
new file mode 100644
index 00000000..a5c8b003
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs
@@ -0,0 +1,288 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Platform;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Helper;
+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; }
+
+ public delegate void UpdateBoundsCallbackDelegate(Rect rect);
+ 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")]
+ private IPlatformHandle CreateLinux(IPlatformHandle control)
+ {
+ if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
+ {
+ X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
+ X11Window.Hide();
+ }
+ 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)
+ {
+ if (msg == WindowsMessages.LBUTTONDOWN ||
+ msg == WindowsMessages.RBUTTONDOWN ||
+ msg == WindowsMessages.LBUTTONUP ||
+ msg == WindowsMessages.RBUTTONUP ||
+ msg == WindowsMessages.MOUSEMOVE)
+ {
+ 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()
+ {
+ // Create a new CAMetalLayer.
+ IntPtr layerClass = ObjectiveC.objc_getClass("CAMetalLayer");
+ IntPtr metalLayer = ObjectiveC.IntPtr_objc_msgSend(layerClass, "alloc");
+ ObjectiveC.objc_msgSend(metalLayer, "init");
+
+ // Create a child NSView to render into.
+ IntPtr nsViewClass = ObjectiveC.objc_getClass("NSView");
+ IntPtr child = ObjectiveC.IntPtr_objc_msgSend(nsViewClass, "alloc");
+ ObjectiveC.objc_msgSend(child, "init", new ObjectiveC.NSRect(0, 0, 0, 0));
+
+ // Make its renderer our metal layer.
+ ObjectiveC.objc_msgSend(child, "setWantsLayer:", 1);
+ ObjectiveC.objc_msgSend(child, "setLayer:", metalLayer);
+ ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
+
+ // Ensure the scale factor is up to date.
+ _updateBoundsCallback = rect =>
+ {
+ ObjectiveC.objc_msgSend(metalLayer, "setContentsScale:", Program.DesktopScaleFactor);
+ };
+
+ IntPtr nsView = child;
+ MetalLayer = metalLayer;
+ 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()
+ {
+ // TODO
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs
new file mode 100644
index 00000000..305e891a
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs
@@ -0,0 +1,87 @@
+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;
+using SPB.Platform.WGL;
+using SPB.Windowing;
+using System;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+ public class EmbeddedWindowOpenGL : EmbeddedWindow
+ {
+ private SwappableNativeWindowBase _window;
+
+ public OpenGLContextBase Context { get; set; }
+
+ public EmbeddedWindowOpenGL() { }
+
+ 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 (ConfigurationState.Instance.Logger.GraphicsDebugLevel != GraphicsDebugLevel.None)
+ {
+ flags |= OpenGLContextFlags.Debug;
+ }
+
+ 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);
+
+ GL.LoadBindings(new OpenTKBindingsContext(Context.GetProcAddress));
+
+ Context.MakeCurrent(null);
+ }
+
+ public void MakeCurrent()
+ {
+ Context?.MakeCurrent(_window);
+ }
+
+ public void MakeCurrent(NativeWindowBase window)
+ {
+ Context?.MakeCurrent(window);
+ }
+
+ public void 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/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs b/src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs
new file mode 100644
index 00000000..0b3eb9e3
--- /dev/null
+++ b/src/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/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs b/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs
new file mode 100644
index 00000000..a2ec02b2
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs
@@ -0,0 +1,20 @@
+using OpenTK;
+using System;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+ internal class OpenTKBindingsContext : IBindingsContext
+ {
+ private readonly Func<string, IntPtr> _getProcAddress;
+
+ public OpenTKBindingsContext(Func<string, IntPtr> getProcAddress)
+ {
+ _getProcAddress = getProcAddress;
+ }
+
+ public IntPtr GetProcAddress(string procName)
+ {
+ return _getProcAddress(procName);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml
new file mode 100644
index 00000000..bb96b10d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml
@@ -0,0 +1,11 @@
+<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.Renderer.RendererHost"
+ Focusable="True">
+</UserControl> \ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
new file mode 100644
index 00000000..ee10282d
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs
@@ -0,0 +1,68 @@
+using Avalonia;
+using Avalonia.Controls;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Ui.Common.Configuration;
+using System;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+ public partial class RendererHost : UserControl, IDisposable
+ {
+ public readonly EmbeddedWindow EmbeddedWindow;
+
+ public event EventHandler<EventArgs> WindowCreated;
+ public event Action<object, Size> SizeChanged;
+
+ public RendererHost()
+ {
+ InitializeComponent();
+
+ 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;
+ }
+
+ GC.SuppressFinalize(this);
+ }
+
+ 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/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs b/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs
new file mode 100644
index 00000000..e090f14c
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs
@@ -0,0 +1,47 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.OpenGL;
+using SPB.Graphics;
+using SPB.Graphics.OpenGL;
+using SPB.Platform;
+using SPB.Windowing;
+
+namespace Ryujinx.Ava.UI.Renderer
+{
+ class SPBOpenGLContext : IOpenGLContext
+ {
+ private readonly OpenGLContextBase _context;
+ private readonly NativeWindowBase _window;
+
+ private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window)
+ {
+ _context = context;
+ _window = window;
+ }
+
+ public void Dispose()
+ {
+ _context.Dispose();
+ _window.Dispose();
+ }
+
+ public void MakeCurrent()
+ {
+ _context.MakeCurrent(_window);
+ }
+
+ 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);
+
+ context.Initialize(window);
+ context.MakeCurrent(window);
+
+ GL.LoadBindings(new OpenTKBindingsContext(context.GetProcAddress));
+
+ context.MakeCurrent(null);
+
+ return new SPBOpenGLContext(context, window);
+ }
+ }
+}