aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Ava/Ui/Controls
diff options
context:
space:
mode:
authorEmmanuel Hansen <emmausssss@gmail.com>2022-08-16 16:32:37 +0000
committerGitHub <noreply@github.com>2022-08-16 16:32:37 +0000
commitc8f9292babd5aa6021ce1bd6a977130baebb7de3 (patch)
treee6e62d79d08726129485476078732f9b89f6ab5c /Ryujinx.Ava/Ui/Controls
parent0ec933a6152ebb7724da1e3a05a5ae1c2ea07b2f (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.cs6
-rw-r--r--Ryujinx.Ava/Ui/Controls/RendererControl.cs28
-rw-r--r--Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs107
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());
}
}
}