Avalonia - Use embedded window for avalonia (#3674)

* wip

* use embedded window

* fix race condition on opengl Windows

* fix glx issues on prime nvidia

* fix mouse support win32

* clean up

* addressed review

* addressed review

* fix warnings

* fix sotware keyboard dialog

* Update Ryujinx.Ava/Ui/Applet/SwkbdAppletDialog.axaml.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* remove double semi

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
This commit is contained in:
Emmanuel Hansen 2022-09-19 18:05:26 +00:00 committed by GitHub
parent b9f1ff3c77
commit 6f0395538b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 868 additions and 3531 deletions

View file

@ -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;