diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index d5d5f211e9..6ea067d8b7 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Avalonia.Controls; using Avalonia.Android; using Avalonia.Android.Platform; @@ -22,6 +24,36 @@ namespace Avalonia .UseSkia(); } } + + public enum AndroidRenderingMode + { + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 1, + + /// + /// Enables android EGL rendering. + /// + Egl = 2 + } + + public sealed class AndroidPlatformOptions + { + /// + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . + /// + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + AndroidRenderingMode.Egl, AndroidRenderingMode.Software + }; + } } namespace Avalonia.Android @@ -47,18 +79,39 @@ namespace Avalonia.Android .Bind().ToConstant(new ChoreographerTimer()) .Bind().ToSingleton(); - if (Options.UseGpu) + var graphics = InitializeGraphics(Options); + if (graphics is not null) { - EglPlatformGraphics.TryInitialize(); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics); } - - Compositor = new Compositor(AvaloniaLocator.Current.GetService()); + + Compositor = new Compositor(graphics); } - } + + private static IPlatformGraphics InitializeGraphics(AndroidPlatformOptions opts) + { + if (opts.RenderingMode is null || !opts.RenderingMode.Any()) + { + throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} must not be empty or null"); + } - public sealed class AndroidPlatformOptions - { - public bool UseDeferredRendering { get; set; } = false; - public bool UseGpu { get; set; } = true; + foreach (var renderingMode in opts.RenderingMode) + { + if (renderingMode == AndroidRenderingMode.Software) + { + return null; + } + + if (renderingMode == AndroidRenderingMode.Egl) + { + if (EglPlatformGraphics.TryCreate() is { } egl) + { + return egl; + } + } + } + + throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); + } } } diff --git a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs index 07e304febe..cf81999095 100644 --- a/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs +++ b/src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs @@ -16,13 +16,6 @@ namespace Avalonia.OpenGL.Egl _display = display; } - public static void TryInitialize() - { - var feature = TryCreate(); - if (feature != null) - AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); - } - public static EglPlatformGraphics? TryCreate() => TryCreate(() => new EglDisplay(new EglDisplayCreationOptions { Egl = new EglInterface(), diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs index 06766e0963..58336522c1 100644 --- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs +++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs @@ -15,18 +15,6 @@ namespace Avalonia.X11.Glx IPlatformGraphicsContext IPlatformGraphics.CreateContext() => Display.CreateContext(); public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); - - public static bool TryInitialize(X11Info x11, IList glProfiles) - { - var feature = TryCreate(x11, glProfiles); - if (feature != null) - { - AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature); - return true; - } - - return false; - } public static GlxPlatformGraphics TryCreate(X11Info x11, IList glProfiles) { diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index a880e4ba1a..7324481aaa 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -93,17 +93,13 @@ namespace Avalonia.X11 XI2 = xi2; } - if (options.UseGpu) + var graphics = InitializeGraphics(options, Info); + if (graphics is not null) { - if (options.UseEGL) - EglPlatformGraphics.TryInitialize(); - else - GlxPlatformGraphics.TryInitialize(Info, Options.GlProfiles); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics); } - var gl = AvaloniaLocator.Current.GetService(); - - Compositor = new Compositor(gl); + Compositor = new Compositor(graphics); } public IntPtr DeferredDisplay { get; set; } @@ -185,25 +181,81 @@ namespace Avalonia.X11 return false; } + + private static IPlatformGraphics InitializeGraphics(X11PlatformOptions opts, X11Info info) + { + if (opts.RenderingMode is null || !opts.RenderingMode.Any()) + { + throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} must not be empty or null"); + } + + foreach (var renderingMode in opts.RenderingMode) + { + if (renderingMode == X11RenderingMode.Software) + { + return null; + } + + if (renderingMode == X11RenderingMode.Glx) + { + if (GlxPlatformGraphics.TryCreate(info, opts.GlProfiles) is { } glx) + { + return glx; + } + } + + if (renderingMode == X11RenderingMode.Egl) + { + if (EglPlatformGraphics.TryCreate() is { } egl) + { + return egl; + } + } + } + + throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); + } } } namespace Avalonia { + public enum X11RenderingMode + { + /// + /// Avalonia is rendered into a framebuffer. + /// + Software = 1, + + /// + /// Enables Glx rendering. + /// + Glx = 2, + + /// + /// Enables native Linux EGL rendering. + /// + Egl = 3 + } + /// /// Platform-specific options which apply to Linux. /// public class X11PlatformOptions { /// - /// Enables native Linux EGL when set to true. The default value is false. + /// Gets or sets Avalonia rendering modes with fallbacks. + /// The first element in the array has the highest priority. + /// The default value is: , . /// - public bool UseEGL { get; set; } - - /// - /// Determines whether to use GPU for rendering in your project. The default value is true. - /// - public bool UseGpu { get; set; } = true; + /// + /// If application should work on as wide range of devices as possible, at least add as a fallback value. + /// + /// Thrown if no values were matched. + public IReadOnlyList RenderingMode { get; set; } = new[] + { + X11RenderingMode.Glx, X11RenderingMode.Software + }; /// /// Embeds popups to the window when set to true. The default value is false. diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs index 9eeb36b7d1..f4765a558f 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs @@ -19,7 +19,7 @@ public enum Win32RenderingMode /// /// Avalonia would try to use native Widows OpenGL with GPU rendering. /// - Wgl + Wgl = 3 } public enum Win32CompositionMode