diff options
| author | Emmanuel Hansen <emmausssss@gmail.com> | 2022-08-16 16:32:37 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-16 16:32:37 +0000 |
| commit | c8f9292babd5aa6021ce1bd6a977130baebb7de3 (patch) | |
| tree | e6e62d79d08726129485476078732f9b89f6ab5c /Ryujinx.Ava/Ui/Controls | |
| parent | 0ec933a6152ebb7724da1e3a05a5ae1c2ea07b2f (diff) | |
Avalonia - Couple fixes and improvements to vulkan (#3483)
* drop split devices, rebase
* add fallback to opengl if vulkan is not available
* addressed review
* ensure present image references are incremented and decremented when necessary
* allow changing vsync for vulkan
* fix screenshot on avalonia vulkan
* save favorite when toggled
* improve sync between popups
* use separate devices for each new window
* fix crash when closing window
* addressed review
* don't create the main window with immediate mode
* change skia vk delegate to method
* update vulkan throwonerror
* addressed review
Diffstat (limited to 'Ryujinx.Ava/Ui/Controls')
| -rw-r--r-- | Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs | 6 | ||||
| -rw-r--r-- | Ryujinx.Ava/Ui/Controls/RendererControl.cs | 28 | ||||
| -rw-r--r-- | Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs | 107 |
3 files changed, 92 insertions, 49 deletions
diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs index db9caca1..e58bdaa0 100644 --- a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs +++ b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Controls public override void DestroyBackgroundContext() { - _image = null; + Image = null; if (_fence != IntPtr.Zero) { @@ -57,6 +57,8 @@ namespace Ryujinx.Ava.Ui.Controls Dispatcher.UIThread.InvokeAsync(() => { Image = (int)image; + + InvalidateVisual(); }).Wait(); if (_fence != IntPtr.Zero) @@ -66,7 +68,7 @@ namespace Ryujinx.Ava.Ui.Controls _fence = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); - QueueRender(); + InvalidateVisual(); _gameBackgroundWindow.SwapBuffers(); } diff --git a/Ryujinx.Ava/Ui/Controls/RendererControl.cs b/Ryujinx.Ava/Ui/Controls/RendererControl.cs index 130348f2..392f67e3 100644 --- a/Ryujinx.Ava/Ui/Controls/RendererControl.cs +++ b/Ryujinx.Ava/Ui/Controls/RendererControl.cs @@ -11,25 +11,7 @@ namespace Ryujinx.Ava.Ui.Controls { internal abstract class RendererControl : Control { - protected object _image; - - static RendererControl() - { - AffectsRender<RendererControl>(ImageProperty); - } - - public readonly static StyledProperty<object> ImageProperty = - AvaloniaProperty.Register<RendererControl, object>( - nameof(Image), - 0, - inherits: true, - defaultBindingMode: BindingMode.TwoWay); - - protected object Image - { - get => _image; - set => SetAndRaise(ImageProperty, ref _image, value); - } + protected object Image { get; set; } public event EventHandler<EventArgs> RendererInitialized; public event EventHandler<Size> SizeChanged; @@ -60,8 +42,6 @@ namespace Ryujinx.Ava.Ui.Controls if (!rect.IsEmpty) { RenderSize = rect.Size * VisualRoot.RenderScaling; - - DrawOperation?.Dispose(); DrawOperation = CreateDrawOperation(); } } @@ -97,17 +77,11 @@ namespace Ryujinx.Ava.Ui.Controls RendererInitialized?.Invoke(this, EventArgs.Empty); } - public void QueueRender() - { - Program.RenderTimer.TickNow(); - } - internal abstract void Present(object image); internal void Start() { IsStarted = true; - QueueRender(); } internal void Stop() diff --git a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs index fdbd8df9..7b7dfaa1 100644 --- a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs +++ b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Skia; @@ -11,20 +12,23 @@ using Silk.NET.Vulkan; using SkiaSharp; using SPB.Windowing; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Concurrent; namespace Ryujinx.Ava.Ui.Controls { internal class VulkanRendererControl : RendererControl { + private const int MaxImagesInFlight = 3; + private VulkanPlatformInterface _platformInterface; + private ConcurrentQueue<PresentImageInfo> _imagesInFlight; + private PresentImageInfo _currentImage; public VulkanRendererControl(GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel) { _platformInterface = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>(); + + _imagesInFlight = new ConcurrentQueue<PresentImageInfo>(); } public override void DestroyBackgroundContext() @@ -37,6 +41,40 @@ namespace Ryujinx.Ava.Ui.Controls return new VulkanDrawOperation(this); } + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + _imagesInFlight.Clear(); + + if (_platformInterface.MainSurface.Display != null) + { + _platformInterface.MainSurface.Display.Presented -= Window_Presented; + } + + _currentImage?.Put(); + _currentImage = null; + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + _platformInterface.MainSurface.Display.Presented += Window_Presented; + } + + private void Window_Presented(object sender, EventArgs e) + { + _platformInterface.MainSurface.Device.QueueWaitIdle(); + _currentImage?.Put(); + _currentImage = null; + } + + public override void Render(DrawingContext context) + { + base.Render(context); + } + protected override void CreateWindow() { } @@ -51,12 +89,29 @@ namespace Ryujinx.Ava.Ui.Controls internal override void Present(object image) { - Dispatcher.UIThread.InvokeAsync(() => + Image = image; + + _imagesInFlight.Enqueue((PresentImageInfo)image); + + if (_imagesInFlight.Count > MaxImagesInFlight) + { + _imagesInFlight.TryDequeue(out _); + } + + Dispatcher.UIThread.Post(InvalidateVisual); + } + + private PresentImageInfo GetImage() + { + lock (_imagesInFlight) { - Image = image; - }).Wait(); + if (!_imagesInFlight.TryDequeue(out _currentImage)) + { + _currentImage = (PresentImageInfo)Image; + } - QueueRender(); + return _currentImage; + } } private class VulkanDrawOperation : ICustomDrawOperation @@ -64,6 +119,7 @@ namespace Ryujinx.Ava.Ui.Controls public Rect Bounds { get; } private readonly VulkanRendererControl _control; + private bool _isDestroyed; public VulkanDrawOperation(VulkanRendererControl control) { @@ -73,7 +129,12 @@ namespace Ryujinx.Ava.Ui.Controls public void Dispose() { + if (_isDestroyed) + { + return; + } + _isDestroyed = true; } public bool Equals(ICustomDrawOperation other) @@ -86,30 +147,33 @@ namespace Ryujinx.Ava.Ui.Controls return Bounds.Contains(p); } - public void Render(IDrawingContextImpl context) + public unsafe void Render(IDrawingContextImpl context) { - if (_control.Image == null || _control.RenderSize.Width == 0 || _control.RenderSize.Height == 0) + if (_isDestroyed || _control.Image == null || _control.RenderSize.Width == 0 || _control.RenderSize.Height == 0 || + context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) { return; } - var image = (PresentImageInfo)_control.Image; + var image = _control.GetImage(); - if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) + if (!image.State.IsValid) { + _control._currentImage = null; + return; } - _control._platformInterface.Device.QueueWaitIdle(); - var gpu = AvaloniaLocator.Current.GetService<VulkanSkiaGpu>(); + image.Get(); + var imageInfo = new GRVkImageInfo() { CurrentQueueFamily = _control._platformInterface.PhysicalDevice.QueueFamilyIndex, Format = (uint)Format.R8G8B8A8Unorm, Image = image.Image.Handle, - ImageLayout = (uint)ImageLayout.ColorAttachmentOptimal, + ImageLayout = (uint)ImageLayout.TransferSrcOptimal, ImageTiling = (uint)ImageTiling.Optimal, ImageUsageFlags = (uint)(ImageUsageFlags.ImageUsageColorAttachmentBit | ImageUsageFlags.ImageUsageTransferSrcBit @@ -127,13 +191,15 @@ namespace Ryujinx.Ava.Ui.Controls }; using var backendTexture = new GRBackendRenderTarget( - (int)_control.RenderSize.Width, - (int)_control.RenderSize.Height, + (int)image.Extent.Width, + (int)image.Extent.Height, 1, imageInfo); + + var vulkan = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>(); using var surface = SKSurface.Create( - gpu.GrContext, + skiaDrawingContextImpl.GrContext, backendTexture, GRSurfaceOrigin.TopLeft, SKColorType.Rgba8888); @@ -143,10 +209,11 @@ namespace Ryujinx.Ava.Ui.Controls return; } - var rect = new Rect(new Point(), _control.RenderSize); + var rect = new Rect(new Point(), new Size(image.Extent.Width, image.Extent.Height)); using var snapshot = surface.Snapshot(); - skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint()); + skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), + new SKPaint()); } } } |
