diff options
Diffstat (limited to 'Ryujinx.Ava/Ui/Controls')
-rw-r--r-- | Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs | 190 | ||||
-rw-r--r-- | Ryujinx.Ava/Ui/Controls/RendererControl.cs | 212 | ||||
-rw-r--r-- | Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs | 153 |
3 files changed, 372 insertions, 183 deletions
diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs new file mode 100644 index 00000000..db9caca1 --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/OpenGLRendererControl.cs @@ -0,0 +1,190 @@ +using Avalonia; +using Avalonia.OpenGL; +using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using Avalonia.Threading; +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Configuration; +using SkiaSharp; +using SPB.Graphics; +using SPB.Graphics.OpenGL; +using SPB.Platform; +using SPB.Windowing; +using System; + +namespace Ryujinx.Ava.Ui.Controls +{ + internal class OpenGLRendererControl : RendererControl + { + public int Major { get; } + public int Minor { get; } + public OpenGLContextBase GameContext { get; set; } + + public static OpenGLContextBase PrimaryContext => + AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>() + .PrimaryContext.AsOpenGLContextBase(); + + private SwappableNativeWindowBase _gameBackgroundWindow; + + private IntPtr _fence; + + public OpenGLRendererControl(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel) + { + Major = major; + Minor = minor; + } + + public override void DestroyBackgroundContext() + { + _image = null; + + if (_fence != IntPtr.Zero) + { + DrawOperation.Dispose(); + GL.DeleteSync(_fence); + } + + GlDrawOperation.DeleteFramebuffer(); + + GameContext?.Dispose(); + + _gameBackgroundWindow?.Dispose(); + } + + internal override void Present(object image) + { + Dispatcher.UIThread.InvokeAsync(() => + { + Image = (int)image; + }).Wait(); + + if (_fence != IntPtr.Zero) + { + GL.DeleteSync(_fence); + } + + _fence = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); + + QueueRender(); + + _gameBackgroundWindow.SwapBuffers(); + } + + internal override void MakeCurrent() + { + GameContext.MakeCurrent(_gameBackgroundWindow); + } + + internal override void MakeCurrent(SwappableNativeWindowBase window) + { + GameContext.MakeCurrent(window); + } + + protected override void CreateWindow() + { + var flags = OpenGLContextFlags.Compat; + if (DebugLevel != GraphicsDebugLevel.None) + { + flags |= OpenGLContextFlags.Debug; + } + _gameBackgroundWindow = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); + _gameBackgroundWindow.Hide(); + + GameContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, Major, Minor, flags, shareContext: PrimaryContext); + GameContext.Initialize(_gameBackgroundWindow); + } + + protected override ICustomDrawOperation CreateDrawOperation() + { + return new GlDrawOperation(this); + } + + private class GlDrawOperation : ICustomDrawOperation + { + private static int _framebuffer; + + public Rect Bounds { get; } + + private readonly OpenGLRendererControl _control; + + public GlDrawOperation(OpenGLRendererControl control) + { + _control = control; + Bounds = _control.Bounds; + } + + public void Dispose() { } + + public static void DeleteFramebuffer() + { + if (_framebuffer == 0) + { + GL.DeleteFramebuffer(_framebuffer); + } + + _framebuffer = 0; + } + + public bool Equals(ICustomDrawOperation other) + { + return other is GlDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds; + } + + public bool HitTest(Point p) + { + return Bounds.Contains(p); + } + + private void CreateRenderTarget() + { + _framebuffer = GL.GenFramebuffer(); + } + + public void Render(IDrawingContextImpl context) + { + if (_control.Image == null) + { + return; + } + + if (_framebuffer == 0) + { + CreateRenderTarget(); + } + + int currentFramebuffer = GL.GetInteger(GetPName.FramebufferBinding); + + var image = _control.Image; + var fence = _control._fence; + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); + GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, (int)image, 0); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, currentFramebuffer); + + if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) + { + return; + } + + var imageInfo = new SKImageInfo((int)_control.RenderSize.Width, (int)_control.RenderSize.Height, SKColorType.Rgba8888); + var glInfo = new GRGlFramebufferInfo((uint)_framebuffer, SKColorType.Rgba8888.ToGlSizedFormat()); + + GL.WaitSync(fence, WaitSyncFlags.None, ulong.MaxValue); + + using var backendTexture = new GRBackendRenderTarget(imageInfo.Width, imageInfo.Height, 1, 0, glInfo); + using var surface = SKSurface.Create(skiaDrawingContextImpl.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); + + if (surface == null) + { + return; + } + + var rect = new Rect(new Point(), _control.RenderSize); + + using var snapshot = surface.Snapshot(); + skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint()); + } + } + } +} diff --git a/Ryujinx.Ava/Ui/Controls/RendererControl.cs b/Ryujinx.Ava/Ui/Controls/RendererControl.cs index f81d7e17..130348f2 100644 --- a/Ryujinx.Ava/Ui/Controls/RendererControl.cs +++ b/Ryujinx.Ava/Ui/Controls/RendererControl.cs @@ -2,65 +2,49 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Media; -using Avalonia.OpenGL; -using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; -using Avalonia.Skia; -using Avalonia.Threading; -using OpenTK.Graphics.OpenGL; using Ryujinx.Common.Configuration; -using SkiaSharp; -using SPB.Graphics; -using SPB.Graphics.OpenGL; -using SPB.Platform; using SPB.Windowing; using System; namespace Ryujinx.Ava.Ui.Controls { - internal class RendererControl : Control + internal abstract class RendererControl : Control { - private int _image; + protected object _image; static RendererControl() { AffectsRender<RendererControl>(ImageProperty); } - public readonly static StyledProperty<int> ImageProperty = - AvaloniaProperty.Register<RendererControl, int>(nameof(Image), 0, inherits: true, defaultBindingMode: BindingMode.TwoWay); + public readonly static StyledProperty<object> ImageProperty = + AvaloniaProperty.Register<RendererControl, object>( + nameof(Image), + 0, + inherits: true, + defaultBindingMode: BindingMode.TwoWay); - protected int Image + protected object Image { get => _image; set => SetAndRaise(ImageProperty, ref _image, value); } - public event EventHandler<EventArgs> GlInitialized; + public event EventHandler<EventArgs> RendererInitialized; public event EventHandler<Size> SizeChanged; protected Size RenderSize { get; private set; } public bool IsStarted { get; private set; } - public int Major { get; } - public int Minor { get; } public GraphicsDebugLevel DebugLevel { get; } - public OpenGLContextBase GameContext { get; set; } - - public static OpenGLContextBase PrimaryContext => AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>().PrimaryContext.AsOpenGLContextBase(); - - private SwappableNativeWindowBase _gameBackgroundWindow; private bool _isInitialized; - private IntPtr _fence; - - private GlDrawOperation _glDrawOperation; + protected ICustomDrawOperation DrawOperation { get; private set; } - public RendererControl(int major, int minor, GraphicsDebugLevel graphicsDebugLevel) + public RendererControl(GraphicsDebugLevel graphicsDebugLevel) { - Major = major; - Minor = minor; DebugLevel = graphicsDebugLevel; IObservable<Rect> resizeObservable = this.GetObservable(BoundsProperty); @@ -69,7 +53,7 @@ namespace Ryujinx.Ava.Ui.Controls Focusable = true; } - private void Resized(Rect rect) + protected void Resized(Rect rect) { SizeChanged?.Invoke(this, rect.Size); @@ -77,37 +61,40 @@ namespace Ryujinx.Ava.Ui.Controls { RenderSize = rect.Size * VisualRoot.RenderScaling; - _glDrawOperation?.Dispose(); - _glDrawOperation = new GlDrawOperation(this); + DrawOperation?.Dispose(); + DrawOperation = CreateDrawOperation(); } } + protected abstract ICustomDrawOperation CreateDrawOperation(); + protected abstract void CreateWindow(); + public override void Render(DrawingContext context) { if (!_isInitialized) { CreateWindow(); - OnGlInitialized(); + OnRendererInitialized(); _isInitialized = true; } - if (GameContext == null || !IsStarted || Image == 0) + if (!IsStarted || Image == null) { return; } - if (_glDrawOperation != null) + if (DrawOperation != null) { - context.Custom(_glDrawOperation); + context.Custom(DrawOperation); } base.Render(context); } - protected void OnGlInitialized() + protected void OnRendererInitialized() { - GlInitialized?.Invoke(this, EventArgs.Empty); + RendererInitialized?.Invoke(this, EventArgs.Empty); } public void QueueRender() @@ -115,24 +102,7 @@ namespace Ryujinx.Ava.Ui.Controls Program.RenderTimer.TickNow(); } - internal void Present(object image) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Image = (int)image; - }).Wait(); - - if (_fence != IntPtr.Zero) - { - GL.DeleteSync(_fence); - } - - _fence = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); - - QueueRender(); - - _gameBackgroundWindow.SwapBuffers(); - } + internal abstract void Present(object image); internal void Start() { @@ -145,132 +115,8 @@ namespace Ryujinx.Ava.Ui.Controls IsStarted = false; } - public void DestroyBackgroundContext() - { - _image = 0; - - if (_fence != IntPtr.Zero) - { - _glDrawOperation.Dispose(); - GL.DeleteSync(_fence); - } - - GlDrawOperation.DeleteFramebuffer(); - - GameContext?.Dispose(); - - _gameBackgroundWindow?.Dispose(); - } - - internal void MakeCurrent() - { - GameContext.MakeCurrent(_gameBackgroundWindow); - } - - internal void MakeCurrent(SwappableNativeWindowBase window) - { - GameContext.MakeCurrent(window); - } - - protected void CreateWindow() - { - var flags = OpenGLContextFlags.Compat; - if (DebugLevel != GraphicsDebugLevel.None) - { - flags |= OpenGLContextFlags.Debug; - } - _gameBackgroundWindow = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); - _gameBackgroundWindow.Hide(); - - GameContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, Major, Minor, flags, shareContext: PrimaryContext); - GameContext.Initialize(_gameBackgroundWindow); - } - - private class GlDrawOperation : ICustomDrawOperation - { - private static int _framebuffer; - - public Rect Bounds { get; } - - private readonly RendererControl _control; - - public GlDrawOperation(RendererControl control) - { - _control = control; - Bounds = _control.Bounds; - } - - public void Dispose() { } - - public static void DeleteFramebuffer() - { - if (_framebuffer == 0) - { - GL.DeleteFramebuffer(_framebuffer); - } - - _framebuffer = 0; - } - - public bool Equals(ICustomDrawOperation other) - { - return other is GlDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds; - } - - public bool HitTest(Point p) - { - return Bounds.Contains(p); - } - - private void CreateRenderTarget() - { - _framebuffer = GL.GenFramebuffer(); - } - - public void Render(IDrawingContextImpl context) - { - if (_control.Image == 0) - { - return; - } - - if (_framebuffer == 0) - { - CreateRenderTarget(); - } - - int currentFramebuffer = GL.GetInteger(GetPName.FramebufferBinding); - - var image = _control.Image; - var fence = _control._fence; - - GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer); - GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, image, 0); - GL.BindFramebuffer(FramebufferTarget.Framebuffer, currentFramebuffer); - - if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) - { - return; - } - - var imageInfo = new SKImageInfo((int)_control.RenderSize.Width, (int)_control.RenderSize.Height, SKColorType.Rgba8888); - var glInfo = new GRGlFramebufferInfo((uint)_framebuffer, SKColorType.Rgba8888.ToGlSizedFormat()); - - GL.WaitSync(fence, WaitSyncFlags.None, ulong.MaxValue); - - using var backendTexture = new GRBackendRenderTarget(imageInfo.Width, imageInfo.Height, 1, 0, glInfo); - using var surface = SKSurface.Create(skiaDrawingContextImpl.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); - - if (surface == null) - { - return; - } - - var rect = new Rect(new Point(), _control.RenderSize); - - using var snapshot = surface.Snapshot(); - skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint()); - } - } + public abstract void DestroyBackgroundContext(); + internal abstract void MakeCurrent(); + internal abstract void MakeCurrent(SwappableNativeWindowBase window); } -} +}
\ No newline at end of file diff --git a/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs new file mode 100644 index 00000000..fdbd8df9 --- /dev/null +++ b/Ryujinx.Ava/Ui/Controls/VulkanRendererControl.cs @@ -0,0 +1,153 @@ +using Avalonia; +using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using Avalonia.Threading; +using Ryujinx.Ava.Ui.Backend.Vulkan; +using Ryujinx.Ava.Ui.Vulkan; +using Ryujinx.Common.Configuration; +using Ryujinx.Graphics.Vulkan; +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; + +namespace Ryujinx.Ava.Ui.Controls +{ + internal class VulkanRendererControl : RendererControl + { + private VulkanPlatformInterface _platformInterface; + + public VulkanRendererControl(GraphicsDebugLevel graphicsDebugLevel) : base(graphicsDebugLevel) + { + _platformInterface = AvaloniaLocator.Current.GetService<VulkanPlatformInterface>(); + } + + public override void DestroyBackgroundContext() + { + + } + + protected override ICustomDrawOperation CreateDrawOperation() + { + return new VulkanDrawOperation(this); + } + + protected override void CreateWindow() + { + } + + internal override void MakeCurrent() + { + } + + internal override void MakeCurrent(SwappableNativeWindowBase window) + { + } + + internal override void Present(object image) + { + Dispatcher.UIThread.InvokeAsync(() => + { + Image = image; + }).Wait(); + + QueueRender(); + } + + private class VulkanDrawOperation : ICustomDrawOperation + { + public Rect Bounds { get; } + + private readonly VulkanRendererControl _control; + + public VulkanDrawOperation(VulkanRendererControl control) + { + _control = control; + Bounds = _control.Bounds; + } + + public void Dispose() + { + + } + + public bool Equals(ICustomDrawOperation other) + { + return other is VulkanDrawOperation operation && Equals(this, operation) && operation.Bounds == Bounds; + } + + public bool HitTest(Point p) + { + return Bounds.Contains(p); + } + + public void Render(IDrawingContextImpl context) + { + if (_control.Image == null || _control.RenderSize.Width == 0 || _control.RenderSize.Height == 0) + { + return; + } + + var image = (PresentImageInfo)_control.Image; + + if (context is not ISkiaDrawingContextImpl skiaDrawingContextImpl) + { + return; + } + + _control._platformInterface.Device.QueueWaitIdle(); + + var gpu = AvaloniaLocator.Current.GetService<VulkanSkiaGpu>(); + + var imageInfo = new GRVkImageInfo() + { + CurrentQueueFamily = _control._platformInterface.PhysicalDevice.QueueFamilyIndex, + Format = (uint)Format.R8G8B8A8Unorm, + Image = image.Image.Handle, + ImageLayout = (uint)ImageLayout.ColorAttachmentOptimal, + ImageTiling = (uint)ImageTiling.Optimal, + ImageUsageFlags = (uint)(ImageUsageFlags.ImageUsageColorAttachmentBit + | ImageUsageFlags.ImageUsageTransferSrcBit + | ImageUsageFlags.ImageUsageTransferDstBit), + LevelCount = 1, + SampleCount = 1, + Protected = false, + Alloc = new GRVkAlloc() + { + Memory = image.Memory.Handle, + Flags = 0, + Offset = image.MemoryOffset, + Size = image.MemorySize + } + }; + + using var backendTexture = new GRBackendRenderTarget( + (int)_control.RenderSize.Width, + (int)_control.RenderSize.Height, + 1, + imageInfo); + + using var surface = SKSurface.Create( + gpu.GrContext, + backendTexture, + GRSurfaceOrigin.TopLeft, + SKColorType.Rgba8888); + + if (surface == null) + { + return; + } + + var rect = new Rect(new Point(), _control.RenderSize); + + using var snapshot = surface.Snapshot(); + skiaDrawingContextImpl.SkCanvas.DrawImage(snapshot, rect.ToSKRect(), _control.Bounds.ToSKRect(), new SKPaint()); + } + } + } +} |