Browse Source

Merge branch 'master' into textInputMethodClientRework

pull/11848/head
Benedikt Stebner 3 years ago
committed by GitHub
parent
commit
c956d50af2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      samples/ControlCatalog.NetCore/Program.cs
  2. 74
      src/Android/Avalonia.Android/AndroidPlatform.cs
  3. 4
      src/Avalonia.Base/CombinedGeometry.cs
  4. 2
      src/Avalonia.Base/Media/EllipseGeometry.cs
  5. 7
      src/Avalonia.Base/Media/Geometry.cs
  6. 4
      src/Avalonia.Base/Media/GeometryGroup.cs
  7. 2
      src/Avalonia.Base/Media/LineGeometry.cs
  8. 4
      src/Avalonia.Base/Media/PathGeometry.cs
  9. 4
      src/Avalonia.Base/Media/PlatformGeometry.cs
  10. 2
      src/Avalonia.Base/Media/PolylineGeometry.cs
  11. 2
      src/Avalonia.Base/Media/RectangleGeometry.cs
  12. 4
      src/Avalonia.Base/Media/StreamGeometry.cs
  13. 13
      src/Avalonia.Base/Point.cs
  14. 14
      src/Avalonia.Base/Vector.cs
  15. 24
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  16. 6
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  17. 2
      src/Avalonia.Native/PopupImpl.cs
  18. 2
      src/Avalonia.Native/WindowImpl.cs
  19. 5
      src/Avalonia.Native/WindowImplBase.cs
  20. 7
      src/Avalonia.OpenGL/Egl/EglPlatformGraphics.cs
  21. 12
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  22. 83
      src/Avalonia.X11/X11Platform.cs
  23. 4
      src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
  24. 18
      src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs
  25. 101
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  26. 95
      src/Windows/Avalonia.Win32/Win32Platform.cs
  27. 140
      src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
  28. 2
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
  29. 1
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
  30. 31
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
  31. 2
      tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs

3
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);
}

74
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.");
}
}
}

4
src/Avalonia.Base/CombinedGeometry.cs

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Text;
using Avalonia.Platform;
#nullable enable
namespace Avalonia.Media
{
public enum GeometryCombineMode
@ -147,7 +145,7 @@ namespace Avalonia.Media
return new CombinedGeometry(GeometryCombineMode, Geometry1, Geometry2, Transform);
}
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var g1 = Geometry1;
var g2 = Geometry2;

2
src/Avalonia.Base/Media/EllipseGeometry.cs

@ -135,7 +135,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();

7
src/Avalonia.Base/Media/Geometry.cs

@ -28,6 +28,11 @@ namespace Avalonia.Media
TransformProperty.Changed.AddClassHandler<Geometry>((x,e) => x.TransformChanged(e));
}
internal Geometry()
{
}
/// <summary>
/// Raised when the geometry changes.
/// </summary>
@ -134,7 +139,7 @@ namespace Avalonia.Media
/// Creates the platform implementation of the geometry, without the transform applied.
/// </summary>
/// <returns></returns>
protected abstract IGeometryImpl? CreateDefiningGeometry();
private protected abstract IGeometryImpl? CreateDefiningGeometry();
/// <summary>
/// Invalidates the platform implementation of the geometry.

4
src/Avalonia.Base/Media/GeometryGroup.cs

@ -1,8 +1,6 @@
using Avalonia.Metadata;
using Avalonia.Platform;
#nullable enable
namespace Avalonia.Media
{
/// <summary>
@ -72,7 +70,7 @@ namespace Avalonia.Media
newChildren.Parent = this;
}
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
if (_children.Count > 0)
{

2
src/Avalonia.Base/Media/LineGeometry.cs

@ -68,7 +68,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();

4
src/Avalonia.Base/Media/PathGeometry.cs

@ -43,7 +43,7 @@ namespace Avalonia.Media
/// </summary>
/// <param name="pathData">The s.</param>
/// <returns></returns>
public static new PathGeometry Parse(string pathData)
public new static PathGeometry Parse(string pathData)
{
var pathGeometry = new PathGeometry();
@ -81,7 +81,7 @@ namespace Avalonia.Media
set { SetValue(FillRuleProperty, value); }
}
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var figures = Figures;

4
src/Avalonia.Base/Media/PlatformGeometry.cs

@ -2,7 +2,7 @@
namespace Avalonia.Media
{
internal class PlatformGeometry : Geometry
internal sealed class PlatformGeometry : Geometry
{
private readonly IGeometryImpl _geometryImpl;
@ -16,7 +16,7 @@ namespace Avalonia.Media
return new PlatformGeometry(_geometryImpl);
}
protected override IGeometryImpl? CreateDefiningGeometry()
private protected override IGeometryImpl? CreateDefiningGeometry()
{
return _geometryImpl;
}

2
src/Avalonia.Base/Media/PolylineGeometry.cs

@ -74,7 +74,7 @@ namespace Avalonia.Media
return new PolylineGeometry(Points, IsFilled);
}
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
var geometry = factory.CreateStreamGeometry();

2
src/Avalonia.Base/Media/RectangleGeometry.cs

@ -47,7 +47,7 @@ namespace Avalonia.Media
/// <inheritdoc/>
public override Geometry Clone() => new RectangleGeometry(Rect);
protected override IGeometryImpl? CreateDefiningGeometry()
private protected sealed override IGeometryImpl? CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();

4
src/Avalonia.Base/Media/StreamGeometry.cs

@ -31,7 +31,7 @@ namespace Avalonia.Media
/// </summary>
/// <param name="s">The string.</param>
/// <returns>A <see cref="StreamGeometry"/>.</returns>
public static new StreamGeometry Parse(string s)
public new static StreamGeometry Parse(string s)
{
var streamGeometry = new StreamGeometry();
@ -62,7 +62,7 @@ namespace Avalonia.Media
}
/// <inheritdoc/>
protected override IGeometryImpl? CreateDefiningGeometry()
private protected override IGeometryImpl? CreateDefiningGeometry()
{
if (_impl == null)
{

13
src/Avalonia.Base/Point.cs

@ -164,6 +164,19 @@ namespace Avalonia
/// <returns>The resulting point.</returns>
public static Point operator *(Point point, Matrix matrix) => matrix.Transform(point);
/// <summary>
/// Computes the Euclidean distance between the two given points.
/// </summary>
/// <param name="value1">The first point.</param>
/// <param name="value2">The second point.</param>
/// <returns>The Euclidean distance.</returns>
public static double Distance(Point value1, Point value2)
{
double distanceSquared = ((value2.X - value1.X) * (value2.X - value1.X)) +
((value2.Y - value1.Y) * (value2.Y - value1.Y));
return Math.Sqrt(distanceSquared);
}
/// <summary>
/// Parses a <see cref="Point"/> string.
/// </summary>

14
src/Avalonia.Base/Vector.cs

@ -359,7 +359,7 @@ namespace Avalonia
internal Vector(Vector2 v) : this(v.X, v.Y)
{
}
/// <summary>
@ -379,21 +379,27 @@ namespace Avalonia
/// </summary>
public static Vector Max(Vector left, Vector right) =>
new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y));
/// <summary>
/// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
/// </summary>
public static Vector Min(Vector left, Vector right) =>
new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y));
/// <summary>
/// Computes the Euclidean distance between the two given points.
/// </summary>
/// <param name="value1">The first point.</param>
/// <param name="value2">The second point.</param>
/// <returns>The Euclidean distance.</returns>
public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2));
/// <summary>
/// Returns the Euclidean distance squared between two specified points
/// </summary>
/// <param name="value1">The first point.</param>
/// <param name="value2">The second point.</param>
/// <returns>The Euclidean distance squared.</returns>
public static double DistanceSquared(Vector value1, Vector value2)
{
var difference = value1 - value2;

24
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);

6
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>

2
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;

2
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;

5
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();

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(),

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)
{

83
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.

4
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;
}
}

18
src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs

@ -324,6 +324,11 @@ namespace Avalonia.Win32.Input
if (IsActive)
{
Client.SetPreeditText(null);
if (Client.SupportsSurroundingText && Client.SurroundingText.AnchorOffset != Client.SurroundingText.CursorOffset)
{
KeyPress(Key.Delete);
}
}
IsComposing = true;
@ -393,6 +398,19 @@ namespace Avalonia.Win32.Input
return (int)(ptr.ToInt64() & 0xffffffff);
}
private void KeyPress(Key key)
{
if (_parent?.Input != null)
{
_parent.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _parent.Owner,
RawKeyEventType.KeyDown, key, RawInputModifiers.None));
_parent.Input(new RawKeyEventArgs(KeyboardDevice.Instance!, (ulong)DateTime.Now.Ticks, _parent.Owner,
RawKeyEventType.KeyUp, key, RawInputModifiers.None));
}
}
~Imm32InputMethod()
{
_caretManager.TryDestroy();

101
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.");
}
}

95
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
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; }
}

2
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
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);

31
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);

2
tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs

@ -103,7 +103,7 @@ namespace Avalonia.Base.UnitTests.Media
throw new NotImplementedException();
}
protected override IGeometryImpl CreateDefiningGeometry()
private protected sealed override IGeometryImpl CreateDefiningGeometry()
{
return Mock.Of<IGeometryImpl>(
x => x.WithTransform(It.IsAny<Matrix>()) ==

Loading…
Cancel
Save