diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Ava/UI/Renderer | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Ava/UI/Renderer')
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/EmbeddedWindow.cs | 288 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowOpenGL.cs | 87 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/EmbeddedWindowVulkan.cs | 42 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/OpenTKBindingsContext.cs | 20 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml | 11 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/RendererHost.axaml.cs | 68 | ||||
| -rw-r--r-- | src/Ryujinx.Ava/UI/Renderer/SPBOpenGLContext.cs | 47 |
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); + } + } +} |
