From 223a67543335365de9e7746ad5e9ad568a64f319 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Fri, 3 Jun 2022 18:50:32 +0300 Subject: [PATCH] IPlatformGpu --- src/Avalonia.Base/Platform/IPlatformGpu.cs | 16 +++++ .../Rendering/Composition/Compositor.cs | 4 +- .../Server/ServerCompositionTarget.cs | 22 +++---- .../Composition/Server/ServerCompositor.cs | 6 +- .../AvaloniaNativePlatformOpenGlInterface.cs | 2 + .../Egl/EglPlatformOpenGlInterface.cs | 2 + src/Avalonia.OpenGL/IGlContext.cs | 3 +- .../IPlatformOpenGlInterface.cs | 6 +- src/Avalonia.X11/Glx/GlxPlatformFeature.cs | 2 + src/Avalonia.X11/X11Platform.cs | 6 +- .../OpenGl/WglPlatformOpenGlInterface.cs | 2 + src/Windows/Avalonia.Win32/Win32GlManager.cs | 61 +++++++++++-------- src/Windows/Avalonia.Win32/Win32Platform.cs | 6 +- src/iOS/Avalonia.iOS/EaglDisplay.cs | 2 + 14 files changed, 92 insertions(+), 48 deletions(-) create mode 100644 src/Avalonia.Base/Platform/IPlatformGpu.cs diff --git a/src/Avalonia.Base/Platform/IPlatformGpu.cs b/src/Avalonia.Base/Platform/IPlatformGpu.cs new file mode 100644 index 0000000000..0507dea1d7 --- /dev/null +++ b/src/Avalonia.Base/Platform/IPlatformGpu.cs @@ -0,0 +1,16 @@ +using System; +using Avalonia.Metadata; + +namespace Avalonia.Platform; + +[Unstable] +public interface IPlatformGpu +{ + IPlatformGpuContext PrimaryContext { get; } +} + +[Unstable] +public interface IPlatformGpuContext : IDisposable +{ + IDisposable EnsureCurrent(); +} \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.cs index 96564f0800..03aebba907 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.cs @@ -24,9 +24,9 @@ namespace Avalonia.Rendering.Composition internal ServerCompositor Server => _server; internal CompositionEasingFunction DefaultEasing { get; } - public Compositor(IRenderLoop loop) + public Compositor(IRenderLoop loop, IPlatformGpu? gpu) { - _server = new ServerCompositor(loop, _batchObjectPool, _batchMemoryPool); + _server = new ServerCompositor(loop, gpu, _batchObjectPool, _batchMemoryPool); _implicitBatchCommit = ImplicitBatchCommit; DefaultEasing = new CubicBezierEasingFunction(this, new Vector2(0.25f, 0.1f), new Vector2(0.25f, 1f)); diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index fb0d6fce60..d1d7e421a4 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -93,12 +93,6 @@ namespace Avalonia.Rendering.Composition.Server _redrawRequested = false; using (var targetContext = _renderTarget.CreateDrawingContext(null)) { - // This is a hack to safely dispose layer created by some other render target - // because we can only dispose layers with the corresponding GPU context being - // active on the current thread - while (Compositor.LayersToDispose.Count > 0) - Compositor.LayersToDispose.Dequeue().Dispose(); - var layerSize = Size * Scaling; if (layerSize != _layerSize || _layer == null) { @@ -164,14 +158,20 @@ namespace Avalonia.Rendering.Composition.Server public void Dispose() { + if(_disposed) + return; _disposed = true; - if (_layer != null) + using (_compositor.GpuContext?.EnsureCurrent()) { - Compositor.LayersToDispose.Enqueue(_layer); - _layer = null; + if (_layer != null) + { + _layer.Dispose(); + _layer = null; + } + + _renderTarget?.Dispose(); + _renderTarget = null; } - _renderTarget?.Dispose(); - _renderTarget = null; } public void AddVisual(ServerCompositionVisual visual) diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs index 241be479ff..c44150f756 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs @@ -20,10 +20,12 @@ namespace Avalonia.Rendering.Composition.Server private List _animationsToUpdate = new(); private BatchStreamObjectPool _batchObjectPool; private BatchStreamMemoryPool _batchMemoryPool; - public Queue LayersToDispose { get; } = new(); + public IPlatformGpuContext? GpuContext { get; } - public ServerCompositor(IRenderLoop renderLoop, BatchStreamObjectPool batchObjectPool, BatchStreamMemoryPool batchMemoryPool) + public ServerCompositor(IRenderLoop renderLoop, IPlatformGpu? platformGpu, + BatchStreamObjectPool batchObjectPool, BatchStreamMemoryPool batchMemoryPool) { + GpuContext = platformGpu?.PrimaryContext; _renderLoop = renderLoop; _batchObjectPool = batchObjectPool; _batchMemoryPool = batchMemoryPool; diff --git a/src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs b/src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs index 3b3d8836fd..14d27a90e9 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs @@ -3,6 +3,7 @@ using Avalonia.OpenGL; using Avalonia.Native.Interop; using System.Drawing; using Avalonia.OpenGL.Surfaces; +using Avalonia.Platform; using Avalonia.Threading; namespace Avalonia.Native @@ -37,6 +38,7 @@ namespace Avalonia.Native internal GlContext MainContext { get; } public IGlContext PrimaryContext => MainContext; + IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; public bool CanShareContexts => true; public bool CanCreateContexts => true; diff --git a/src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs b/src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs index 476f65a774..a6d8c1e98d 100644 --- a/src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Logging; +using Avalonia.Platform; using static Avalonia.OpenGL.Egl.EglConsts; namespace Avalonia.OpenGL.Egl @@ -12,6 +13,7 @@ namespace Avalonia.OpenGL.Egl public EglContext PrimaryEglContext { get; } public IGlContext PrimaryContext => PrimaryEglContext; + IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; public EglPlatformOpenGlInterface(EglDisplay display) { diff --git a/src/Avalonia.OpenGL/IGlContext.cs b/src/Avalonia.OpenGL/IGlContext.cs index 50868db873..a52a6535da 100644 --- a/src/Avalonia.OpenGL/IGlContext.cs +++ b/src/Avalonia.OpenGL/IGlContext.cs @@ -1,8 +1,9 @@ using System; +using Avalonia.Platform; namespace Avalonia.OpenGL { - public interface IGlContext : IDisposable + public interface IGlContext : IPlatformGpuContext { GlVersion Version { get; } GlInterface GlInterface { get; } diff --git a/src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs b/src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs index 5ee5df1e85..4ff7997b03 100644 --- a/src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs +++ b/src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs @@ -1,8 +1,10 @@ +using Avalonia.Platform; + namespace Avalonia.OpenGL { - public interface IPlatformOpenGlInterface + public interface IPlatformOpenGlInterface : IPlatformGpu { - IGlContext PrimaryContext { get; } + new IGlContext PrimaryContext { get; } IGlContext CreateSharedContext(); bool CanShareContexts { get; } bool CanCreateContexts { get; } diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs index 6735a32ffe..0968adc799 100644 --- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs +++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Avalonia.Logging; using Avalonia.OpenGL; +using Avalonia.Platform; namespace Avalonia.X11.Glx { @@ -14,6 +15,7 @@ namespace Avalonia.X11.Glx public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryContext); public GlxContext DeferredContext { get; private set; } public IGlContext PrimaryContext => DeferredContext; + IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; public static bool TryInitialize(X11Info x11, IList glProfiles) { diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 9c82288c8e..d882450259 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -103,8 +103,12 @@ namespace Avalonia.X11 GlxPlatformOpenGlInterface.TryInitialize(Info, Options.GlProfiles); } + var gl = AvaloniaLocator.Current.GetService(); + if (gl != null) + AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); + if (options.UseCompositor) - Compositor = new Compositor(AvaloniaLocator.Current.GetService()!); + Compositor = new Compositor(AvaloniaLocator.Current.GetService()!, gl); } diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs index b948495b99..1d0880a468 100644 --- a/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs +++ b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs @@ -2,12 +2,14 @@ using System; using System.Linq; using Avalonia.Logging; using Avalonia.OpenGL; +using Avalonia.Platform; namespace Avalonia.Win32.OpenGl { class WglPlatformOpenGlInterface : IPlatformOpenGlInterface { public WglContext PrimaryContext { get; } + IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; IGlContext IPlatformOpenGlInterface.PrimaryContext => PrimaryContext; public IGlContext CreateSharedContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, PrimaryContext); diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index 2a3f4a3384..39a742d1ac 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -1,6 +1,7 @@ using Avalonia.OpenGL; using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Egl; +using Avalonia.Platform; using Avalonia.Win32.OpenGl; using Avalonia.Win32.WinRT.Composition; @@ -9,45 +10,53 @@ namespace Avalonia.Win32 static class Win32GlManager { - public static void Initialize() + public static IPlatformOpenGlInterface Initialize() { - AvaloniaLocator.CurrentMutable.Bind().ToLazy(() => + var gl = InitializeCore(); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl); + return gl; + } + + static IPlatformOpenGlInterface InitializeCore() + { + + var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); + if (opts.UseWgl) { - var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); - if (opts.UseWgl) - { - var wgl = WglPlatformOpenGlInterface.TryCreate(); - return wgl; - } + var wgl = WglPlatformOpenGlInterface.TryCreate(); + return wgl; + } - if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7) - { - var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); + if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7) + { + var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); - if (egl != null) + if (egl != null) + { + if (opts.EglRendererBlacklist != null) { - if (opts.EglRendererBlacklist != null) + foreach (var item in opts.EglRendererBlacklist) { - foreach (var item in opts.EglRendererBlacklist) + if (egl.PrimaryEglContext.GlInterface.Renderer.Contains(item)) { - if (egl.PrimaryEglContext.GlInterface.Renderer.Contains(item)) - { - return null; - } + return null; } } - - if (opts.UseWindowsUIComposition) - { - WinUICompositorConnection.TryCreateAndRegister(egl, opts.CompositionBackdropCornerRadius); - } } - return egl; + if (opts.UseWindowsUIComposition) + { + WinUICompositorConnection.TryCreateAndRegister(egl, opts.CompositionBackdropCornerRadius); + } } - return null; - }); + return egl; + } + + return null; } + + } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 4b0350f40f..af38a92e26 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -180,15 +180,15 @@ namespace Avalonia.Win32 .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()) .Bind().ToConstant(s_instance); - Win32GlManager.Initialize(); + var gl = Win32GlManager.Initialize(); _uiThread = Thread.CurrentThread; if (OleContext.Current != null) AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); - + if (Options.UseCompositor) - Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService()); + Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), gl); } public bool HasMessages() diff --git a/src/iOS/Avalonia.iOS/EaglDisplay.cs b/src/iOS/Avalonia.iOS/EaglDisplay.cs index bd1969081d..906bbc29e7 100644 --- a/src/iOS/Avalonia.iOS/EaglDisplay.cs +++ b/src/iOS/Avalonia.iOS/EaglDisplay.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Disposables; using Avalonia.OpenGL; +using Avalonia.Platform; using OpenGLES; namespace Avalonia.iOS @@ -9,6 +10,7 @@ namespace Avalonia.iOS { public IGlContext PrimaryContext => Context; public IGlContext CreateSharedContext() => throw new NotSupportedException(); + IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; public bool CanShareContexts => false; public bool CanCreateContexts => false; public IGlContext CreateContext() => throw new System.NotSupportedException();