From aee40789aa3144154c25a78956324193a0a677b0 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Wed, 16 Aug 2023 22:55:55 +0200 Subject: [PATCH] Win32+Angle: reuse the first created D3D11 display --- .../Angle/AngleWin32PlatformGraphics.cs | 143 ------------------ .../AngleWin32PlatformGraphicsFactory.cs | 48 ++++++ .../Angle/D3D11AngleWin32PlatformGraphics.cs | 91 +++++++++++ .../Angle/D3D9AngleWin32PlatformGraphics.cs | 53 +++++++ src/Windows/Avalonia.Win32/Win32GlManager.cs | 4 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 5 +- 6 files changed, 196 insertions(+), 148 deletions(-) delete mode 100644 src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs create mode 100644 src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphicsFactory.cs create mode 100644 src/Windows/Avalonia.Win32/OpenGl/Angle/D3D11AngleWin32PlatformGraphics.cs create mode 100644 src/Windows/Avalonia.Win32/OpenGl/Angle/D3D9AngleWin32PlatformGraphics.cs diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs deleted file mode 100644 index 8ee6d286d1a..00000000000 --- a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Avalonia.Logging; -using Avalonia.OpenGL; -using Avalonia.OpenGL.Angle; -using Avalonia.OpenGL.Egl; -using Avalonia.Platform; - -namespace Avalonia.Win32.OpenGl.Angle; - -internal class AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphicsOpenGlContextFactory -{ - private readonly Win32AngleEglInterface _egl; - private readonly AngleWin32EglDisplay? _sharedDisplay; - private EglContext? _sharedContext; - - [MemberNotNullWhen(true, nameof(_sharedDisplay))] - public bool UsesSharedContext => _sharedDisplay is not null; - - public AngleOptions.PlatformApi PlatformApi { get; } - - public IPlatformGraphicsContext CreateContext() - { - if (UsesSharedContext) - throw new InvalidOperationException(); - - var display = AngleWin32EglDisplay.CreateD3D11Display(_egl); - var success = false; - try - { - var rv = display.CreateContext(new EglContextOptions - { - DisposeCallback = display.Dispose, - ExtraFeatures = new Dictionary> - { - [typeof(IGlPlatformSurfaceRenderTargetFactory)] = _ => new AngleD3DTextureFeature(), - [typeof(IGlContextExternalObjectsFeature)] = context => new AngleExternalObjectsFeature(context) - } - }); - success = true; - return rv; - } - finally - { - if (!success) - display.Dispose(); - } - } - - public IPlatformGraphicsContext GetSharedContext() - { - if (!UsesSharedContext) - throw new InvalidOperationException(); - if (_sharedContext == null || _sharedContext.IsLost) - { - _sharedContext?.Dispose(); - _sharedContext = null; - _sharedContext = _sharedDisplay.CreateContext(new EglContextOptions()); - } - - return _sharedContext; - } - - public AngleWin32PlatformGraphics(Win32AngleEglInterface egl, AngleWin32EglDisplay display) - : this(egl, display.PlatformApi) - { - _sharedDisplay = display; - } - - public AngleWin32PlatformGraphics(Win32AngleEglInterface egl, AngleOptions.PlatformApi api) - { - _egl = egl; - PlatformApi = api; - } - - - public static AngleWin32PlatformGraphics? TryCreate(AngleOptions? options) - { - Win32AngleEglInterface egl; - try - { - egl = new(); - } - catch (Exception e) - { - Logger.TryGet(LogEventLevel.Error, "OpenGL") - ?.Log(null, "Unable to load ANGLE: {0}", e); - return null; - } - - foreach (var api in (options?.AllowedPlatformApis ?? new [] - { - AngleOptions.PlatformApi.DirectX11 - }).Distinct()) - if (api == AngleOptions.PlatformApi.DirectX11) - { - try - { - using var display = AngleWin32EglDisplay.CreateD3D11Display(egl); - using var ctx = display.CreateContext(new EglContextOptions()); - ctx.MakeCurrent().Dispose(); - } - catch (Exception e) - { - Logger.TryGet(LogEventLevel.Error, "OpenGL") - ?.Log(null, "Unable to initialize ANGLE-based rendering with DirectX11 : {0}", e); - continue; - } - - return new AngleWin32PlatformGraphics(egl, AngleOptions.PlatformApi.DirectX11); - } - else - { - AngleWin32EglDisplay? sharedDisplay = null; - try - { - sharedDisplay = AngleWin32EglDisplay.CreateD3D9Display(egl); - using (var ctx = sharedDisplay.CreateContext(new EglContextOptions())) - ctx.MakeCurrent().Dispose(); - - return new AngleWin32PlatformGraphics(egl, sharedDisplay); - } - catch (Exception e) - { - sharedDisplay?.Dispose(); - Logger.TryGet(LogEventLevel.Error, "OpenGL") - ?.Log(null, "Unable to initialize ANGLE-based rendering with DirectX9 : {0}", e); - } - } - return null; - } - - public IGlContext CreateContext(IEnumerable? versions) - { - if (UsesSharedContext) - throw new InvalidOperationException(); - if (versions != null && versions.All(v => v.Type != GlProfileType.OpenGLES || v.Major != 3)) - throw new OpenGlException("Unable to create context with requested version"); - return (IGlContext)CreateContext(); - } -} diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphicsFactory.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphicsFactory.cs new file mode 100644 index 00000000000..8f58e7796c6 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphicsFactory.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using Avalonia.Logging; +using Avalonia.OpenGL.Angle; +using Avalonia.Platform; + +namespace Avalonia.Win32.OpenGl.Angle; + +internal static class AngleWin32PlatformGraphicsFactory +{ + public static IPlatformGraphics? TryCreate(AngleOptions? options) + { + Win32AngleEglInterface egl; + try + { + egl = new(); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, "OpenGL") + ?.Log(null, "Unable to load ANGLE: {0}", e); + return null; + } + + var allowedPlatformApis = options?.AllowedPlatformApis ?? new[] { AngleOptions.PlatformApi.DirectX11 }; + + foreach (var api in allowedPlatformApis.Distinct()) + { + switch (api) + { + case AngleOptions.PlatformApi.DirectX11 + when D3D11AngleWin32PlatformGraphics.TryCreate(egl) is { } platformGraphics: + return platformGraphics; + + case AngleOptions.PlatformApi.DirectX9 + when D3D9AngleWin32PlatformGraphics.TryCreate(egl) is { } platformGraphics: + return platformGraphics; + + default: + Logger.TryGet(LogEventLevel.Error, "OpenGL") + ?.Log(null, "Unknown requested PlatformApi {0}", api); + break; + } + } + + return null; + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D11AngleWin32PlatformGraphics.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D11AngleWin32PlatformGraphics.cs new file mode 100644 index 00000000000..05cfc9629f7 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D11AngleWin32PlatformGraphics.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Avalonia.Logging; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Angle; +using Avalonia.OpenGL.Egl; +using Avalonia.Platform; + +namespace Avalonia.Win32.OpenGl.Angle; + +internal sealed class D3D11AngleWin32PlatformGraphics : IPlatformGraphics, IPlatformGraphicsOpenGlContextFactory +{ + private readonly Win32AngleEglInterface _egl; + private AngleWin32EglDisplay? _initialDisplay; + + public D3D11AngleWin32PlatformGraphics(Win32AngleEglInterface egl, AngleWin32EglDisplay? initialDisplay) + { + _egl = egl; + _initialDisplay = initialDisplay; + } + + public bool UsesSharedContext + => false; + + public IPlatformGraphicsContext CreateContext() + { + var display = Interlocked.Exchange(ref _initialDisplay, null); + if (display is { IsLost: true }) + display = null; + + display ??= AngleWin32EglDisplay.CreateD3D11Display(_egl); + return CreateContextForDisplay(display); + } + + private static EglContext CreateContextForDisplay(AngleWin32EglDisplay display) + { + var success = false; + try + { + var context = display.CreateContext(new EglContextOptions + { + DisposeCallback = display.Dispose, + ExtraFeatures = new Dictionary> + { + [typeof(IGlPlatformSurfaceRenderTargetFactory)] = _ => new AngleD3DTextureFeature(), + [typeof(IGlContextExternalObjectsFeature)] = context => new AngleExternalObjectsFeature(context) + } + }); + success = true; + return context; + } + finally + { + if (!success) + display.Dispose(); + } + } + + public IGlContext CreateContext(IEnumerable? versions) + { + if (versions is not null && versions.All(v => v.Type != GlProfileType.OpenGLES || v.Major != 3)) + throw new OpenGlException("Unable to create context with requested version"); + + return (IGlContext)CreateContext(); + } + + IPlatformGraphicsContext IPlatformGraphics.GetSharedContext() + => throw new InvalidOperationException(); + + public static D3D11AngleWin32PlatformGraphics? TryCreate(Win32AngleEglInterface egl) + { + AngleWin32EglDisplay? display = null; + try + { + display = AngleWin32EglDisplay.CreateD3D11Display(egl); + using var ctx = display.CreateContext(new EglContextOptions()); + ctx.MakeCurrent().Dispose(); + } + catch (Exception e) + { + display?.Dispose(); + Logger.TryGet(LogEventLevel.Error, "OpenGL") + ?.Log(null, "Unable to initialize ANGLE-based rendering with DirectX11 : {0}", e); + return null; + } + + return new D3D11AngleWin32PlatformGraphics(egl, display); + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D9AngleWin32PlatformGraphics.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D9AngleWin32PlatformGraphics.cs new file mode 100644 index 00000000000..07e2a854792 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/D3D9AngleWin32PlatformGraphics.cs @@ -0,0 +1,53 @@ +using System; +using Avalonia.Logging; +using Avalonia.OpenGL.Angle; +using Avalonia.OpenGL.Egl; +using Avalonia.Platform; + +namespace Avalonia.Win32.OpenGl.Angle; + +internal sealed class D3D9AngleWin32PlatformGraphics : IPlatformGraphics +{ + private readonly AngleWin32EglDisplay _sharedDisplay; + private EglContext? _sharedContext; + + public D3D9AngleWin32PlatformGraphics(AngleWin32EglDisplay sharedDisplay) + => _sharedDisplay = sharedDisplay; + + public bool UsesSharedContext + => true; + + public IPlatformGraphicsContext GetSharedContext() + { + if (_sharedContext is { IsLost: true }) + { + _sharedContext.Dispose(); + _sharedContext = null; + } + + return _sharedContext ??= _sharedDisplay.CreateContext(new EglContextOptions()); + } + + IPlatformGraphicsContext IPlatformGraphics.CreateContext() + => throw new InvalidOperationException(); + + public static D3D9AngleWin32PlatformGraphics? TryCreate(Win32AngleEglInterface egl) + { + AngleWin32EglDisplay? sharedDisplay = null; + try + { + sharedDisplay = AngleWin32EglDisplay.CreateD3D9Display(egl); + using var ctx = sharedDisplay.CreateContext(new EglContextOptions()); + ctx.MakeCurrent().Dispose(); + } + catch (Exception e) + { + sharedDisplay?.Dispose(); + Logger.TryGet(LogEventLevel.Error, "OpenGL") + ?.Log(null, "Unable to initialize ANGLE-based rendering with DirectX9 : {0}", e); + return null; + } + + return new D3D9AngleWin32PlatformGraphics(sharedDisplay); + } +} diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index ebb708f4c50..26a55a98dfc 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -45,9 +45,9 @@ static class Win32GlManager if (renderingMode == Win32RenderingMode.AngleEgl) { - var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ?? new()); + var egl = AngleWin32PlatformGraphicsFactory.TryCreate(AvaloniaLocator.Current.GetService() ?? new()); - if (egl != null && egl.PlatformApi == AngleOptions.PlatformApi.DirectX11) + if (egl is D3D11AngleWin32PlatformGraphics) { TryRegisterComposition(opts); return egl; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 0a75732182f..ca0e287fe4c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -135,8 +135,7 @@ public WindowImpl() var compositionConnector = AvaloniaLocator.Current.GetService(); - var isUsingAngleDX11 = glPlatform is AngleWin32PlatformGraphics angle && - angle.PlatformApi == AngleOptions.PlatformApi.DirectX11; + var isUsingAngleDX11 = glPlatform is D3D11AngleWin32PlatformGraphics; _isUsingComposition = compositionConnector is { } && isUsingAngleDX11; DxgiConnection? dxgiConnection = null; @@ -172,7 +171,7 @@ public WindowImpl() } else { - if (glPlatform is AngleWin32PlatformGraphics) + if (glPlatform is D3D11AngleWin32PlatformGraphics or D3D9AngleWin32PlatformGraphics) _gl = new EglGlPlatformSurface(this); else if (glPlatform is WglPlatformOpenGlInterface) _gl = new WglGlPlatformSurface(this);