Browse Source

Merge pull request #11898 from AvaloniaUI/win-platorm-settings-cleanup

Restructure Win32PlatformOptions options
Max Katz 2 years ago
parent
commit
ec6ec09ac9

+ 1 - 2
samples/ControlCatalog.NetCore/Program.cs

@@ -110,8 +110,7 @@ namespace ControlCatalog.NetCore
             {
                 builder.With(new Win32PlatformOptions()
                 {
-                    UseLowLatencyDxgiSwapChain = true,
-                    UseWindowsUIComposition = false
+                    CompositionMode = new [] { Win32CompositionMode.LowLatencyDxgiSwapChain }
                 });
                 return builder.StartWithClassicDesktopLifetime(args);
             }

+ 65 - 9
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,39 @@ namespace Avalonia
                 .UseSkia();
         }
     }
+
+    /// <summary>
+    /// Represents the rendering mode for platform graphics.
+    /// </summary>
+    public enum AndroidRenderingMode
+    {
+        /// <summary>
+        /// Avalonia is rendered into a framebuffer.
+        /// </summary>
+        Software = 1,
+
+        /// <summary>
+        /// Enables android EGL rendering.
+        /// </summary>
+        Egl = 2
+    }
+
+    public sealed class AndroidPlatformOptions
+    {
+        /// <summary>
+        /// Gets or sets Avalonia rendering modes with fallbacks.
+        /// The first element in the array has the highest priority.
+        /// The default value is: <see cref="AndroidRenderingMode.Egl"/>, <see cref="AndroidRenderingMode.Software"/>.
+        /// </summary>
+        /// <remarks>
+        /// If application should work on as wide range of devices as possible, at least add <see cref="AndroidRenderingMode.Software"/> as a fallback value.
+        /// </remarks>
+        /// <exception cref="System.InvalidOperationException">Thrown if no values were matched.</exception>
+        public IReadOnlyList<AndroidRenderingMode> RenderingMode { get; set; } = new[]
+        {
+            AndroidRenderingMode.Egl, AndroidRenderingMode.Software
+        };
+    }
 }
 
 namespace Avalonia.Android
@@ -47,18 +82,39 @@ namespace Avalonia.Android
                 .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
 
-            if (Options.UseGpu)
+            var graphics = InitializeGraphics(Options);
+            if (graphics is not null)
             {
-                EglPlatformGraphics.TryInitialize();
+                AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(graphics);
             }
-            
-            Compositor = new Compositor(AvaloniaLocator.Current.GetService<IPlatformGraphics>());
+
+            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.");
+        }
     }
 }

+ 11 - 13
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -123,20 +123,18 @@ namespace Avalonia.Native
             hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers));
 
             AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(hotkeys);
-            
-            if (_options.UseGpu)
+
+            // TODO: add software and metal support via RenderingMode options param
+            try
+            {
+                _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
+                AvaloniaLocator.CurrentMutable
+                    .Bind<IPlatformGraphics>().ToConstant(_platformGl);
+
+            }
+            catch (Exception)
             {
-                try
-                {
-                    _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
-                    AvaloniaLocator.CurrentMutable
-                        .Bind<IPlatformGraphics>().ToConstant(_platformGl);
-
-                }
-                catch (Exception)
-                {
-                    // ignored
-                }
+                // ignored
             }
             
             Compositor = new Compositor(_platformGl, true);

+ 1 - 5
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Native;
 
@@ -30,11 +31,6 @@ namespace Avalonia
     /// </summary>
     public class AvaloniaNativePlatformOptions
     {
-        /// <summary>
-        /// Determines whether to use GPU for rendering in your project. The default value is true.
-        /// </summary>
-        public bool UseGpu { get; set; } = true;
-
         /// <summary>
         /// Embeds popups to the window when set to true. The default value is false.
         /// </summary>

+ 1 - 1
src/Avalonia.Native/PopupImpl.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Native
         public PopupImpl(IAvaloniaNativeFactory factory,
             AvaloniaNativePlatformOptions opts,
             AvaloniaNativeGlPlatformGraphics glFeature,
-            IWindowBaseImpl parent) : base(factory, opts, glFeature)
+            IWindowBaseImpl parent) : base(factory, glFeature)
         {
             _opts = opts;
             _glFeature = glFeature;

+ 1 - 1
src/Avalonia.Native/WindowImpl.cs

@@ -24,7 +24,7 @@ namespace Avalonia.Native
         private bool _canResize = true;
 
         internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
-            AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature)
+            AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, glFeature)
         {
             _opts = opts;
             _glFeature = glFeature;

+ 2 - 3
src/Avalonia.Native/WindowImplBase.cs

@@ -67,11 +67,10 @@ namespace Avalonia.Native
         private PlatformBehaviorInhibition _platformBehaviorInhibition;
         private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None;
 
-        internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
-            AvaloniaNativeGlPlatformGraphics glFeature)
+        internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativeGlPlatformGraphics glFeature)
         {
             _factory = factory;
-            _gpu = opts.UseGpu && glFeature != null;
+            _gpu = glFeature != null;
 
             _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
             _mouse = new MouseDevice();

+ 0 - 7
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<IPlatformGraphics>().ToConstant(feature);
-        }
-        
         public static EglPlatformGraphics? TryCreate() => TryCreate(() => new EglDisplay(new EglDisplayCreationOptions
         {
             Egl = new EglInterface(),

+ 0 - 12
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<GlVersion> glProfiles)
-        {
-            var feature = TryCreate(x11, glProfiles);
-            if (feature != null)
-            {
-                AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(feature);
-                return true;
-            }
-
-            return false;
-        }
         
         public static GlxPlatformGraphics TryCreate(X11Info x11, IList<GlVersion> glProfiles)
         {

+ 69 - 14
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<IPlatformGraphics>().ToConstant(graphics);
             }
 
-            var gl = AvaloniaLocator.Current.GetService<IPlatformGraphics>();
-
-            Compositor = new Compositor(gl);
+            Compositor = new Compositor(graphics);
         }
 
         public IntPtr DeferredDisplay { get; set; }
@@ -185,25 +181,84 @@ 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
 {
     /// <summary>
-    /// Platform-specific options which apply to Linux.
+    /// Represents the rendering mode for platform graphics.
     /// </summary>
-    public class X11PlatformOptions
+    public enum X11RenderingMode
     {
         /// <summary>
-        /// Enables native Linux EGL when set to true. The default value is false.
+        /// Avalonia is rendered into a framebuffer.
         /// </summary>
-        public bool UseEGL { get; set; }
+        Software = 1,
 
         /// <summary>
-        /// Determines whether to use GPU for rendering in your project. The default value is true.
+        /// Enables Glx rendering.
         /// </summary>
-        public bool UseGpu { get; set; } = true;
+        Glx = 2,
+
+        /// <summary>
+        /// Enables native Linux EGL rendering.
+        /// </summary>
+        Egl = 3
+    }
+    
+    /// <summary>
+    /// Platform-specific options which apply to Linux.
+    /// </summary>
+    public class X11PlatformOptions
+    {
+        /// <summary>
+        /// Gets or sets Avalonia rendering modes with fallbacks.
+        /// The first element in the array has the highest priority.
+        /// The default value is: <see cref="X11RenderingMode.Glx"/>, <see cref="X11RenderingMode.Software"/>.
+        /// </summary>
+        /// <remarks>
+        /// If application should work on as wide range of devices as possible, at least add <see cref="X11RenderingMode.Software"/> as a fallback value.
+        /// </remarks>
+        /// <exception cref="System.InvalidOperationException">Thrown if no values were matched.</exception>
+        public IReadOnlyList<X11RenderingMode> RenderingMode { get; set; } = new[]
+        {
+            X11RenderingMode.Glx, X11RenderingMode.Software
+        };
 
         /// <summary>
         /// Embeds popups to the window when set to true. The default value is false.

+ 3 - 1
src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs

@@ -28,16 +28,18 @@ namespace Avalonia.Win32.DirectX
             _syncLock = syncLock;
         }
         
-        public static void TryCreateAndRegister()
+        public static bool TryCreateAndRegister()
         {
             try
             {
                 TryCreateAndRegisterCore();
+                return true;
             }
             catch (Exception ex)
             {
                 Logger.TryGet(LogEventLevel.Error, LogArea)
                     ?.Log(null, "Unable to establish Dxgi: {0}", ex);
+                return false;
             }
         }
 

+ 68 - 33
src/Windows/Avalonia.Win32/Win32GlManager.cs

@@ -1,3 +1,6 @@
+using System;
+using System.Diagnostics.Tracing;
+using System.Linq;
 using Avalonia.OpenGL;
 using Avalonia.Platform;
 using Avalonia.Win32.DirectX;
@@ -5,56 +8,88 @@ using Avalonia.Win32.OpenGl;
 using Avalonia.Win32.OpenGl.Angle;
 using Avalonia.Win32.WinRT.Composition;
 
-namespace Avalonia.Win32
+namespace Avalonia.Win32;
+
+static class Win32GlManager
 {
-    static class Win32GlManager
+    public static IPlatformGraphics? Initialize()
     {
-        public static IPlatformGraphics? Initialize()
-        {
-            var gl = InitializeCore();
-
-            if (gl is not null)
-            {
-                AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(gl);
-            }
+        var gl = InitializeCore();
 
-            return gl;
+        if (gl is not null)
+        {
+            AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(gl);
         }
+
+        return gl;
+    }
         
-        private static IPlatformGraphics? InitializeCore()
+    private static IPlatformGraphics? InitializeCore()
+    {
+        var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions();
+        if (opts.RenderingMode is null || !opts.RenderingMode.Any())
         {
+            throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} must not be empty or null");
+        }
 
-            var opts = AvaloniaLocator.Current.GetService<Win32PlatformOptions>() ?? new Win32PlatformOptions();
-            if (opts.UseWgl)
+        foreach (var renderingMode in opts.RenderingMode)
+        {
+            if (renderingMode == Win32RenderingMode.Software)
             {
-                var wgl = WglPlatformOpenGlInterface.TryCreate();
-                return wgl;
+                return null;
             }
-
-            if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7)
+                
+            if (renderingMode == Win32RenderingMode.AngleEgl)
             {
-                var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService<AngleOptions>() ??
-                                                               new());
+                var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService<AngleOptions>() ?? new());
 
                 if (egl != null && egl.PlatformApi == AngleOptions.PlatformApi.DirectX11)
                 {
-                    AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphicsOpenGlContextFactory>()
-                        .ToConstant(egl);
-                    
-                    if (opts.UseWindowsUIComposition)
-                    {
-                        WinUiCompositorConnection.TryCreateAndRegister();
-                    }
-                    else if (opts.UseLowLatencyDxgiSwapChain)
-                    {
-                        DxgiConnection.TryCreateAndRegister();
-                    }
+                    TryRegisterComposition(opts);
+                    return egl;
+                }
+            }
+
+            if (renderingMode == Win32RenderingMode.Wgl)
+            {
+                if (WglPlatformOpenGlInterface.TryCreate() is { } wgl)
+                {
+                    return wgl;
                 }
+            }
+        }
+
+        throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
+    }
+
+    private static void TryRegisterComposition(Win32PlatformOptions opts)
+    {
+        if (opts.CompositionMode is null || !opts.CompositionMode.Any())
+        {
+            throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} must not be empty or null");
+        }
+
+        foreach (var compositionMode in opts.CompositionMode)
+        {
+            if (compositionMode == Win32CompositionMode.RedirectionSurface)
+            {
+                return;
+            }
 
-                return egl;
+            if (compositionMode == Win32CompositionMode.WinUIComposition
+                && WinUiCompositorConnection.IsSupported()
+                && WinUiCompositorConnection.TryCreateAndRegister())
+            {
+                return;
             }
 
-            return null;
+            if (compositionMode == Win32CompositionMode.LowLatencyDxgiSwapChain
+                && DxgiConnection.TryCreateAndRegister())
+            {
+                return;
+            }
         }
+            
+        throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CompositionMode)} has a value of \"{string.Join(", ", opts.CompositionMode)}\", but no options were applied.");
     }
 }

+ 18 - 77
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -1,8 +1,8 @@
 using System;
-using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.IO;
+using System.Linq;
 using Avalonia.Reactive;
 using System.Runtime.InteropServices;
 using System.Threading;
@@ -10,7 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.Platform;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
-using Avalonia.OpenGL;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
@@ -31,78 +30,6 @@ namespace Avalonia
                 "Win32");
         }
     }
-
-    /// <summary>
-    /// Platform-specific options which apply to Windows.
-    /// </summary>
-    public class Win32PlatformOptions
-    {
-        /// <summary>
-        /// Enables ANGLE for Windows. For every Windows version that is above Windows 7, the default is true otherwise it's false.
-        /// </summary>
-        /// <remarks>
-        /// GPU rendering will not be enabled if this is set to false.
-        /// </remarks>
-        public bool? AllowEglInitialization { get; set; }
-
-        /// <summary>
-        /// Embeds popups to the window when set to true. The default value is false.
-        /// </summary>
-        public bool OverlayPopups { get; set; }
-
-        /// <summary>
-        /// Avalonia would try to use native Widows OpenGL when set to true. The default value is false.
-        /// </summary>
-        public bool UseWgl { get; set; }
-
-        public IList<GlVersion> WglProfiles { get; set; } = new List<GlVersion>
-        {
-            new GlVersion(GlProfileType.OpenGL, 4, 0),
-            new GlVersion(GlProfileType.OpenGL, 3, 2),
-        };
-
-        /// <summary>
-        /// Render Avalonia to a Texture inside the Windows.UI.Composition tree.
-        /// This setting is true by default.
-        /// </summary>
-        /// <remarks>
-        /// Supported on Windows 10 build 16299 and above. Ignored on other versions.
-        /// This is recommended if you need to use AcrylicBlur or acrylic in your applications.
-        /// </remarks>
-        public bool UseWindowsUIComposition { get; set; } = true;
-
-        /// <summary>
-        /// When <see cref="UseWindowsUIComposition"/> enabled, create rounded corner blur brushes
-        /// If set to null the brushes will be created using default settings (sharp corners)
-        /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app
-        /// </summary>
-        public float? CompositionBackdropCornerRadius { get; set; }
-
-        /// <summary>
-        /// When <see cref="UseLowLatencyDxgiSwapChain"/> is active, renders Avalonia through a low-latency Dxgi Swapchain.
-        /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion. 
-        /// This is only recommended if low input latency is desirable, and there is no need for the transparency
-        /// and stylings / blurrings offered by <see cref="UseWindowsUIComposition"/><br/>
-        /// This is mutually exclusive with 
-        /// <see cref="UseWindowsUIComposition"/> which if active will override this setting.
-        /// This setting is false by default.
-        /// </summary>
-        public bool UseLowLatencyDxgiSwapChain { get; set; }
-
-        /// <summary>
-        /// Render directly on the UI thread instead of using a dedicated render thread.
-        /// Only applicable if both <see cref="UseWindowsUIComposition"/> and <see cref="UseLowLatencyDxgiSwapChain"/>
-        /// are false.
-        /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.
-        /// This setting is false by default.
-        /// </summary>
-        public bool ShouldRenderOnUIThread { get; set; }
-        
-        /// <summary>
-        /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu
-        /// </summary>
-        public IPlatformGraphics? CustomPlatformGraphics { get; set; }
-    }
 }
 
 namespace Avalonia.Win32
@@ -173,9 +100,23 @@ namespace Avalonia.Win32
                 .Bind<NonPumpingLockHelper.IHelperImpl>().ToConstant(NonPumpingWaitHelperImpl.Instance)
                 .Bind<IMountedVolumeInfoProvider>().ToConstant(new WindowsMountedVolumeInfoProvider())
                 .Bind<IPlatformLifetimeEventsImpl>().ToConstant(s_instance);
-            
-            var platformGraphics = options.CustomPlatformGraphics
-                                   ?? Win32GlManager.Initialize();
+
+            IPlatformGraphics? platformGraphics;
+            if (options.CustomPlatformGraphics is not null)
+            {
+                if (options.CompositionMode?.Contains(Win32CompositionMode.RedirectionSurface) == false)
+                {
+                    throw new InvalidOperationException(
+                        $"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.CustomPlatformGraphics)} is only " +
+                        $"compatible with {nameof(Win32CompositionMode)}.{nameof(Win32CompositionMode.RedirectionSurface)}");
+                }
+                
+                platformGraphics = options.CustomPlatformGraphics;
+            }
+            else
+            {
+                platformGraphics = Win32GlManager.Initialize();   
+            }
             
             if (OleContext.Current != null)
                 AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();

+ 140 - 0
src/Windows/Avalonia.Win32/Win32PlatformOptions.cs

@@ -0,0 +1,140 @@
+using System.Collections.Generic;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+
+namespace Avalonia;
+
+/// <summary>
+/// Represents the rendering mode for platform graphics.
+/// </summary>
+public enum Win32RenderingMode
+{
+    /// <summary>
+    /// Avalonia is rendered into a framebuffer.
+    /// </summary>
+    Software = 1,
+
+    /// <summary>
+    /// Enables ANGLE EGL for Windows with GPU rendering.
+    /// </summary>
+    AngleEgl = 2,
+
+    /// <summary>
+    /// Avalonia would try to use native Widows OpenGL with GPU rendering.
+    /// </summary>
+    Wgl = 3
+}
+
+/// <summary>
+/// Represents the Win32 window composition mode.
+/// </summary>
+public enum Win32CompositionMode
+{
+    /// <summary>
+    /// Render Avalonia to a texture inside the Windows.UI.Composition tree.
+    /// </summary>
+    /// <remarks>
+    /// Supported on Windows 10 build 17134 and above. Ignored on other versions.
+    /// This is recommended option, as it allows window acrylic effects and high refresh rate rendering.<br/>
+    /// Can only be applied with <see cref="Win32PlatformOptions.RenderingMode"/>=<see cref="Win32RenderingMode.AngleEgl"/>.
+    /// </remarks>
+    WinUIComposition = 1,
+
+    // /// <summary>
+    // /// Render Avalonia to a texture inside the DirectComposition tree.
+    // /// </summary>
+    // /// <remarks>
+    // /// Supported on Windows 8 and above. Ignored on other versions.<br/>
+    // /// Can only be applied with <see cref="Win32PlatformOptions.RenderingMode"/>=<see cref="Win32RenderingMode.AngleEgl"/>.
+    // /// </remarks>
+    // DirectComposition = 2,
+
+    /// <summary>
+    /// When <see cref="LowLatencyDxgiSwapChain"/> is active, renders Avalonia through a low-latency Dxgi Swapchain.
+    /// </summary>
+    /// <remarks>
+    /// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion. 
+    /// This is only recommended if low input latency is desirable, and there is no need for the transparency
+    /// and styling / blurring offered by <see cref="WinUIComposition"/>.<br/>
+    /// Can only be applied with <see cref="Win32PlatformOptions.RenderingMode"/>=<see cref="Win32RenderingMode.AngleEgl"/>.
+    /// </remarks>
+    LowLatencyDxgiSwapChain = 3,
+
+    /// <summary>
+    /// The window renders to a redirection surface.
+    /// </summary>
+    /// <remarks>
+    /// This option is kept only for compatibility with older systems. Some Avalonia features might not work.
+    /// </remarks>
+    RedirectionSurface,
+}
+
+/// <summary>
+/// Platform-specific options which apply to Windows.
+/// </summary>
+public class Win32PlatformOptions
+{
+    /// <summary>
+    /// Embeds popups to the window when set to true. The default value is false.
+    /// </summary>
+    public bool OverlayPopups { get; set; }
+
+    /// <summary>
+    /// Gets or sets Avalonia rendering modes with fallbacks.
+    /// The first element in the array has the highest priority.
+    /// The default value is: <see cref="Win32RenderingMode.AngleEgl"/>, <see cref="Win32RenderingMode.Software"/>.
+    /// </summary>
+    /// <remarks>
+    /// If application should work on as wide range of devices as possible, at least add <see cref="Win32RenderingMode.Software"/> as a fallback value.
+    /// </remarks>
+    /// <exception cref="System.InvalidOperationException">Thrown if no values were matched.</exception>
+    public IReadOnlyList<Win32RenderingMode> RenderingMode { get; set; } = new[]
+    {
+        Win32RenderingMode.AngleEgl, Win32RenderingMode.Software
+    };
+
+    /// <summary>
+    /// Gets or sets Avalonia composition modes with fallbacks.
+    /// The first element in the array has the highest priority.
+    /// The default value is: <see cref="Win32CompositionMode.WinUIComposition"/>, <see cref="Win32CompositionMode.RedirectionSurface"/>.
+    /// </summary>
+    /// <remarks>
+    /// If application should work on as wide range of devices as possible, at least add <see cref="Win32CompositionMode.RedirectionSurface"/> as a fallback value.
+    /// </remarks>
+    /// <exception cref="System.InvalidOperationException">Thrown if no values were matched.</exception>
+    public IReadOnlyList<Win32CompositionMode> CompositionMode { get; set; } = new[]
+    {
+        Win32CompositionMode.WinUIComposition, Win32CompositionMode.RedirectionSurface
+    };
+
+    /// <summary>
+    /// When <see cref="CompositionMode"/> is set to <see cref="Win32CompositionMode.WinUIComposition"/>, create rounded corner blur brushes
+    /// If set to null the brushes will be created using default settings (sharp corners)
+    /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app.
+    /// </summary>
+    public float? WinUICompositionBackdropCornerRadius { get; set; }
+
+    /// <summary>
+    /// Render directly on the UI thread instead of using a dedicated render thread.
+    /// Only applicable if <see cref="CompositionMode"/> is set to <see cref="Win32CompositionMode.RedirectionSurface"/>.
+    /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF.
+    /// This setting is false by default.
+    /// </summary>
+    public bool ShouldRenderOnUIThread { get; set; }
+
+    /// <summary>
+    /// Windows OpenGL profiles used when <see cref="RenderingMode"/> is set to <see cref="Win32RenderingMode.Wgl"/>.
+    /// This setting is 4.0 and 3.2 by default.
+    /// </summary>
+    public IList<GlVersion> WglProfiles { get; set; } = new List<GlVersion>
+    {
+        new(GlProfileType.OpenGL, 4, 0), new(GlProfileType.OpenGL, 3, 2)
+    };
+
+    /// <summary>
+    /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu.
+    /// When this property set <see cref="RenderingMode"/> is ignored
+    /// and <see cref="CompositionMode"/> only accepts null or <see cref="Win32CompositionMode.RedirectionSurface"/>.
+    /// </summary>
+    public IPlatformGraphics? CustomPlatformGraphics { get; set; }
+}

+ 1 - 1
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Win32.WinRT.Composition
         public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice)
         {
             var cornerRadius = AvaloniaLocator.Current.GetService<Win32PlatformOptions>()
-                ?.CompositionBackdropCornerRadius;
+                ?.WinUICompositionBackdropCornerRadius;
             _window ??= new WinUiCompositedWindow(_info, _shared, cornerRadius);
             _window.SetBlur(_blurEffect);
 

+ 1 - 0
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs

@@ -12,6 +12,7 @@ internal class WinUiCompositionShared : IDisposable
     public ICompositionBrush? MicaBrush { get; }
     public object SyncRoot { get; } = new();
 
+    public static readonly Version MinWinCompositionVersion = new(10, 0, 17134);
     public static readonly Version MinAcrylicVersion = new(10, 0, 15063);
     public static readonly Version MinHostBackdropVersion = new(10, 0, 22000);
     

+ 16 - 15
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs

@@ -112,35 +112,36 @@ internal class WinUiCompositorConnection : IRenderTimer
         }
     }
 
-    public static void TryCreateAndRegister()
+    public static bool IsSupported()
     {
-        const int majorRequired = 10;
-        const int buildRequired = 17134;
-
-        var majorInstalled = Win32Platform.WindowsVersion.Major;
-        var buildInstalled = Win32Platform.WindowsVersion.Build;
-
-        if (majorInstalled >= majorRequired &&
-            buildInstalled >= buildRequired)
+        return Win32Platform.WindowsVersion >= WinUiCompositionShared.MinWinCompositionVersion;
+    }
+    
+    public static bool TryCreateAndRegister()
+    {
+        if (IsSupported())
         {
             try
             {
                 TryCreateAndRegisterCore();
-                return;
+                return true;
             }
             catch (Exception e)
             {
                 Logger.TryGet(LogEventLevel.Error, "WinUIComposition")
                     ?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
-
             }
         }
+        else
+        {
+            var osVersionNotice =
+                $"Windows {WinUiCompositionShared.MinWinCompositionVersion} is required. Your machine has Windows {Win32Platform.WindowsVersion} installed.";
 
-        var osVersionNotice =
-            $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed.";
+            Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null,
+                $"Unable to initialize WinUI compositor: {osVersionNotice}");
+        }
 
-        Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null,
-            $"Unable to initialize WinUI compositor: {osVersionNotice}");
+        return false;
     }
 
     public WinUiCompositedWindowSurface CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new(_shared, info);