diff --git a/.editorconfig b/.editorconfig
index 238e9887bd..ff7ac5d69e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -140,16 +140,22 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion
+# CS0162: Remove unreachable code
+dotnet_diagnostic.CS0162.severity = error
# CA1304: Specify CultureInfo
dotnet_diagnostic.CA1304.severity = warning
# CA1802: Use literals where appropriate
dotnet_diagnostic.CA1802.severity = warning
+# CA1815: Override equals and operator equals on value types
+dotnet_diagnostic.CA1815.severity = warning
# CA1820: Test for empty strings using string length
dotnet_diagnostic.CA1820.severity = warning
# CA1821: Remove empty finalizers
dotnet_diagnostic.CA1821.severity = warning
# CA1822: Mark members as static
dotnet_diagnostic.CA1822.severity = suggestion
+# CA1823: Avoid unused private fields
+dotnet_diagnostic.CA1823.severity = warning
dotnet_code_quality.CA1822.api_surface = private, internal
# CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = warning
diff --git a/Avalonia.sln b/Avalonia.sln
index 7efb294b64..fc42a5d63b 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -100,7 +100,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\ImageSharp.props = build\ImageSharp.props
- build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
@@ -136,8 +135,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
@@ -228,9 +225,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser", "src\Bro
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}"
EndProject
@@ -366,10 +363,6 @@ Global
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU
- {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -580,7 +573,6 @@ Global
{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
- {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098}
diff --git a/build/JetBrains.Annotations.props b/build/JetBrains.Annotations.props
deleted file mode 100644
index 7bc12cbd84..0000000000
--- a/build/JetBrains.Annotations.props
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/nukebuild/DotNetConfigHelper.cs b/nukebuild/DotNetConfigHelper.cs
index eca1e2684d..9d43261616 100644
--- a/nukebuild/DotNetConfigHelper.cs
+++ b/nukebuild/DotNetConfigHelper.cs
@@ -1,5 +1,4 @@
using System.Globalization;
-using JetBrains.Annotations;
using Nuke.Common.Tools.DotNet;
// ReSharper disable ReturnValueOfPureMethodIsNotUsed
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 4a5f5bc96c..9e0fb3f852 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -92,6 +92,9 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml b/samples/ControlCatalog/Pages/CompositionPage.axaml
index 403f45b8eb..602b9b768d 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml
@@ -2,44 +2,57 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="using:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.CompositionPage">
-
- Implicit animations
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Resize me
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Resize me
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
index c70675b606..8b12a2d663 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
@@ -1,28 +1,39 @@
using System;
using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
using Avalonia;
+using Avalonia.Animation;
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
+using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Animations;
using Avalonia.VisualTree;
+using Math = System.Math;
namespace ControlCatalog.Pages;
public partial class CompositionPage : UserControl
{
private ImplicitAnimationCollection? _implicitAnimations;
+ private CompositionCustomVisual? _customVisual;
+ private CompositionSolidColorVisual? _solidVisual;
public CompositionPage()
{
AvaloniaXamlLoader.Load(this);
+ AttachAnimatedSolidVisual(this.FindControl("SolidVisualHost")!);
+ AttachCustomVisual(this.FindControl("CustomVisualHost")!);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
this.Get("Items").Items = CreateColorItems();
+
}
private static List CreateColorItems()
@@ -126,6 +137,167 @@ public partial class CompositionPage : UserControl
compositionVisual.ImplicitAnimations = page._implicitAnimations;
}
}
+
+ void AttachAnimatedSolidVisual(Visual v)
+ {
+ void Update()
+ {
+ if(_solidVisual == null)
+ return;
+ _solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
+ _solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
+ }
+ v.AttachedToVisualTree += delegate
+ {
+ var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+ if(compositor == null || _solidVisual?.Compositor == compositor)
+ return;
+ _solidVisual = compositor.CreateSolidColorVisual();
+ ElementComposition.SetElementChildVisual(v, _solidVisual);
+ _solidVisual.Color = Colors.Red;
+ var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
+ animation.InsertKeyFrame(0, Colors.Red);
+ animation.InsertKeyFrame(0.5f, Colors.Blue);
+ animation.InsertKeyFrame(1, Colors.Green);
+ animation.Duration = TimeSpan.FromSeconds(5);
+ animation.IterationBehavior = AnimationIterationBehavior.Forever;
+ animation.Direction = PlaybackDirection.Alternate;
+ _solidVisual.StartAnimation("Color", animation);
+
+ _solidVisual.AnchorPoint = new Vector2(0, 0);
+
+ var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
+ scale.Duration = TimeSpan.FromSeconds(5);
+ scale.IterationBehavior = AnimationIterationBehavior.Forever;
+ scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
+ scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
+ scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
+
+ _solidVisual.StartAnimation("Scale", scale);
+
+ var center =
+ _solidVisual.Compositor.CreateExpressionAnimation(
+ "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
+ _solidVisual.StartAnimation("CenterPoint", center);
+ Update();
+ };
+ v.PropertyChanged += (_, a) =>
+ {
+ if (a.Property == BoundsProperty)
+ Update();
+ };
+ }
+
+ void AttachCustomVisual(Visual v)
+ {
+ void Update()
+ {
+ if (_customVisual == null)
+ return;
+ var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
+ _customVisual.Size = new Vector2((float)v.Bounds.Width, h);
+ _customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
+ }
+ v.AttachedToVisualTree += delegate
+ {
+ var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+ if(compositor == null || _customVisual?.Compositor == compositor)
+ return;
+ _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
+ ElementComposition.SetElementChildVisual(v, _customVisual);
+ _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
+ Update();
+ };
+
+ v.PropertyChanged += (_, a) =>
+ {
+ if (a.Property == BoundsProperty)
+ Update();
+ };
+ }
+
+ class CustomVisualHandler : CompositionCustomVisualHandler
+ {
+ private TimeSpan _animationElapsed;
+ private TimeSpan? _lastServerTime;
+ private bool _running;
+
+ public static readonly object StopMessage = new(), StartMessage = new();
+
+ public override void OnRender(ImmediateDrawingContext drawingContext)
+ {
+ if (_running)
+ {
+ if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
+ _lastServerTime = CompositionNow;
+ }
+
+ const int cnt = 20;
+ var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
+ var maxPointSizeY = EffectiveSize.Y / 4;
+ var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
+ var animationLength = TimeSpan.FromSeconds(4);
+ var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
+
+ var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
+
+ for (var c = 0; c < cnt; c++)
+ {
+ var stage = (animationStage + (double)c / cnt) % 1;
+ var colorStage =
+ (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
+ var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
+ var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
+ var opacity = Math.Sin(stage * 3.14);
+
+
+ drawingContext.DrawEllipse(new ImmutableSolidColorBrush(Color.FromArgb(
+ 255,
+ (byte)(255 - 255 * colorStage),
+ (byte)(255 * Math.Abs(0.5 - colorStage) * 2),
+ (byte)(255 * colorStage)
+ ), opacity), null,
+ new Point(posX, posY), pointSize / 2, pointSize / 2);
+ }
+
+ }
+
+ public override void OnMessage(object message)
+ {
+ if (message == StartMessage)
+ {
+ _running = true;
+ _lastServerTime = null;
+ RegisterForNextAnimationFrameUpdate();
+ }
+ else if (message == StopMessage)
+ _running = false;
+ }
+
+ public override void OnAnimationFrameUpdate()
+ {
+ if (_running)
+ {
+ Invalidate();
+ RegisterForNextAnimationFrameUpdate();
+ }
+ }
+ }
+
+ private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
+ {
+ Thread.Sleep(10000);
+ }
+
+ private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
+ {
+ _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
+ }
+
+ private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
+ {
+ _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
+ }
}
public class CompositionPageColorItem
diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs
new file mode 100644
index 0000000000..ee10f21317
--- /dev/null
+++ b/samples/ControlCatalog/Pages/GesturePage.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Numerics;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.LogicalTree;
+using Avalonia.Markup.Xaml;
+using Avalonia.Rendering.Composition;
+
+namespace ControlCatalog.Pages
+{
+ public class GesturePage : UserControl
+ {
+ private bool _isInit;
+ private float _currentScale;
+
+ public GesturePage()
+ {
+ this.InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ if(_isInit)
+ {
+ return;
+ }
+
+ _isInit = true;
+
+ SetPullHandlers(this.Find("TopPullZone"), false);
+ SetPullHandlers(this.Find("BottomPullZone"), true);
+ SetPullHandlers(this.Find("RightPullZone"), true);
+ SetPullHandlers(this.Find("LeftPullZone"), false);
+
+ var image = this.Find("PinchImage");
+ SetPinchHandlers(image);
+
+ var reset = this.Find
- 8,5,8,6
-
diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml
index 7a46f21534..da2021790a 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml
@@ -10,8 +10,6 @@
- 8,5,8,6
-
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
index d8f8267fe5..44ca60e2fa 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
@@ -4,8 +4,8 @@
-
-
+
+
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
index eea3d3ad08..a8297953a8 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
@@ -99,15 +99,15 @@ namespace Avalonia.Themes.Fluent
var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight;
var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight;
var dict = Resources.MergedDictionaries;
- if (dict.Count == 2)
+ if (dict.Count == 0)
{
- dict.Insert(1, themeVariantResource1);
+ dict.Add(themeVariantResource1);
dict.Add(themeVariantResource2);
}
else
{
- dict[1] = themeVariantResource1;
- dict[3] = themeVariantResource2;
+ dict[0] = themeVariantResource1;
+ dict[1] = themeVariantResource2;
}
}
diff --git a/src/Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml
index 79d6c6d917..3d52464858 100644
--- a/src/Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
diff --git a/src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml b/src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml
index 61dae9b445..5a1fa4ec6d 100644
--- a/src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml
@@ -11,6 +11,12 @@
+
+
+
+
@@ -54,6 +60,14 @@
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
index ba1b35e2ee..945622f22f 100644
--- a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
@@ -9,17 +9,16 @@
diff --git a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
index 4bad556338..093adaeab2 100644
--- a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
@@ -3,70 +3,70 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml b/src/Avalonia.Themes.Simple/SimpleTheme.xaml
index fe296bd288..5b0cae7fd2 100644
--- a/src/Avalonia.Themes.Simple/SimpleTheme.xaml
+++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml
@@ -4,7 +4,7 @@
-
+
diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
index 56c4cbeac6..42dfafd7e0 100644
--- a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
+++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
@@ -58,13 +58,13 @@ namespace Avalonia.Themes.Simple
{
var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight;
var dict = Resources.MergedDictionaries;
- if (dict.Count == 1)
+ if (dict.Count == 0)
{
dict.Add(themeVariantResource);
}
else
{
- dict[1] = themeVariantResource;
+ dict[0] = themeVariantResource;
}
}
}
diff --git a/src/Avalonia.X11/Glx/GlxContext.cs b/src/Avalonia.X11/Glx/GlxContext.cs
index e9cb88cb8f..def5228e94 100644
--- a/src/Avalonia.X11/Glx/GlxContext.cs
+++ b/src/Avalonia.X11/Glx/GlxContext.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;
using Avalonia.OpenGL;
@@ -70,6 +71,8 @@ namespace Avalonia.X11.Glx
}
public IDisposable MakeCurrent() => MakeCurrent(_defaultXid);
+ public bool IsLost => false;
+
public IDisposable EnsureCurrent()
{
if(IsCurrent)
@@ -86,6 +89,11 @@ namespace Avalonia.X11.Glx
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
+ public bool CanCreateSharedContext => true;
+
+ public IGlContext CreateSharedContext(IEnumerable preferredVersions = null) =>
+ Display.CreateContext(_sharedWith ?? this);
+
public IDisposable MakeCurrent(IntPtr xid)
{
Monitor.Enter(_lock);
@@ -114,5 +122,7 @@ namespace Avalonia.X11.Glx
if (_ownsPBuffer)
Glx.DestroyPbuffer(_x11.Display, _defaultXid);
}
+
+ public object TryGetFeature(Type featureType) => null;
}
}
diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs
index 1e70608168..9b8a8f0b5b 100644
--- a/src/Avalonia.X11/Glx/GlxDisplay.cs
+++ b/src/Avalonia.X11/Glx/GlxDisplay.cs
@@ -111,8 +111,8 @@ namespace Avalonia.X11.Glx
return Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 });
}
-
- public GlxContext CreateContext() => CreateContext();
+ public GlxContext CreateContext() => CreateContext(CreatePBuffer(), null, DeferredContext.SampleCount,
+ DeferredContext.StencilSize, true);
public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true);
diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
index cb4ab4aca0..ebb1e18723 100644
--- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
+++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
@@ -9,20 +9,16 @@ namespace Avalonia.X11.Glx
class GlxGlPlatformSurface: IGlPlatformSurface
{
- private readonly GlxDisplay _display;
- private readonly GlxContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
- public GlxGlPlatformSurface(GlxDisplay display, GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
+ public GlxGlPlatformSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
- _display = display;
- _context = context;
_info = info;
}
- public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
- return new RenderTarget(_context, _info);
+ return new RenderTarget((GlxContext)context, _info);
}
class RenderTarget : IGlPlatformSurfaceRenderTarget
diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
index 0968adc799..bc50fa61aa 100644
--- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
+++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs
@@ -6,38 +6,36 @@ using Avalonia.Platform;
namespace Avalonia.X11.Glx
{
- class GlxPlatformOpenGlInterface : IPlatformOpenGlInterface
+ class GlxPlatformGraphics : IPlatformGraphics
{
public GlxDisplay Display { get; private set; }
public bool CanCreateContexts => true;
public bool CanShareContexts => true;
- public IGlContext CreateContext() => Display.CreateContext();
- public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryContext);
- public GlxContext DeferredContext { get; private set; }
- public IGlContext PrimaryContext => DeferredContext;
- IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext;
+ public bool UsesSharedContext => false;
+ 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);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(feature);
return true;
}
return false;
}
- public static GlxPlatformOpenGlInterface TryCreate(X11Info x11, IList glProfiles)
+ public static GlxPlatformGraphics TryCreate(X11Info x11, IList glProfiles)
{
try
{
var disp = new GlxDisplay(x11, glProfiles);
- return new GlxPlatformOpenGlInterface
+ return new GlxPlatformGraphics
{
- Display = disp,
- DeferredContext = disp.DeferredContext
+ Display = disp
};
}
catch(Exception e)
diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs
index d5eae037a9..4e56ae73cf 100644
--- a/src/Avalonia.X11/NativeDialogs/Gtk.cs
+++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs
@@ -3,7 +3,6 @@ using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Platform.Interop;
-using JetBrains.Annotations;
// ReSharper disable IdentifierTypo
namespace Avalonia.X11.NativeDialogs
diff --git a/src/Avalonia.X11/X11Clipboard.cs b/src/Avalonia.X11/X11Clipboard.cs
index d428d82744..2aa7797067 100644
--- a/src/Avalonia.X11/X11Clipboard.cs
+++ b/src/Avalonia.X11/X11Clipboard.cs
@@ -18,8 +18,6 @@ namespace Avalonia.X11
private TaskCompletionSource _requestedDataTcs;
private readonly IntPtr[] _textAtoms;
private readonly IntPtr _avaloniaSaveTargetsAtom;
- private readonly Dictionary _formatAtoms = new Dictionary();
- private readonly Dictionary _atomFormats = new Dictionary();
public X11Clipboard(AvaloniaX11Platform platform)
{
diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs
index 6041b53a62..16de10e163 100644
--- a/src/Avalonia.X11/X11CursorFactory.cs
+++ b/src/Avalonia.X11/X11CursorFactory.cs
@@ -113,7 +113,8 @@ namespace Avalonia.X11
image->yhot = hotSpot.Y;
image->pixels = (IntPtr)(image + 1);
- using (var renderTarget = platformRenderInterface.CreateRenderTarget(new[] { this }))
+ using (var cpuContext = platformRenderInterface.CreateBackendContext(null))
+ using (var renderTarget = cpuContext.CreateRenderTarget(new[] { this }))
using (var ctx = renderTarget.CreateDrawingContext(null))
{
var r = new Rect(_pixelSize.ToSize(1));
diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs
index 4ae1c1599f..bf59d72c0f 100644
--- a/src/Avalonia.X11/X11IconLoader.cs
+++ b/src/Avalonia.X11/X11IconLoader.cs
@@ -41,7 +41,8 @@ namespace Avalonia.X11
_width = Math.Min(bitmap.PixelSize.Width, 128);
_height = Math.Min(bitmap.PixelSize.Height, 128);
_bdata = new uint[_width * _height];
- using(var rt = AvaloniaLocator.Current.GetService().CreateRenderTarget(new[]{this}))
+ using(var cpuContext = AvaloniaLocator.Current.GetRequiredService().CreateBackendContext(null))
+ using(var rt = cpuContext.CreateRenderTarget(new[]{this}))
using (var ctx = rt.CreateDrawingContext(null))
ctx.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size),
new Rect(0, 0, _width, _height));
diff --git a/src/Avalonia.X11/X11ImmediateRendererProxy.cs b/src/Avalonia.X11/X11ImmediateRendererProxy.cs
index 293e5110f7..ef061cbe5c 100644
--- a/src/Avalonia.X11/X11ImmediateRendererProxy.cs
+++ b/src/Avalonia.X11/X11ImmediateRendererProxy.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
+using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.VisualTree;
@@ -14,11 +16,11 @@ namespace Avalonia.X11
private bool _running;
private object _lock = new object();
- public X11ImmediateRendererProxy(Visual root, IRenderLoop loop)
+ public X11ImmediateRendererProxy(Visual root, IRenderLoop loop, Func renderTargetFactory,
+ PlatformRenderInterfaceContextManager renderContext)
{
_loop = loop;
- _renderer = new ImmediateRenderer(root);
-
+ _renderer = new ImmediateRenderer(root, renderTargetFactory, renderContext);
}
public void Dispose()
@@ -92,6 +94,9 @@ namespace Avalonia.X11
_renderer.Stop();
}
+ public ValueTask TryGetRenderInterfaceFeature(Type featureType) =>
+ _renderer.TryGetRenderInterfaceFeature(featureType);
+
public bool NeedsUpdate => false;
public void Update(TimeSpan time)
{
diff --git a/src/Avalonia.X11/X11Info.cs b/src/Avalonia.X11/X11Info.cs
index 13dc460f45..72f3bf3137 100644
--- a/src/Avalonia.X11/X11Info.cs
+++ b/src/Avalonia.X11/X11Info.cs
@@ -2,7 +2,6 @@ using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
-using JetBrains.Annotations;
using static Avalonia.X11.XLib;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace Avalonia.X11
diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs
index cbb782edd0..e44b5ded14 100644
--- a/src/Avalonia.X11/X11Platform.cs
+++ b/src/Avalonia.X11/X11Platform.cs
@@ -30,6 +30,7 @@ namespace Avalonia.X11
public X11Info Info { get; private set; }
public IX11Screens X11Screens { get; private set; }
public Compositor Compositor { get; private set; }
+ public PlatformRenderInterfaceContextManager RenderInterface { get; private set; }
public IScreenImpl Screens { get; private set; }
public X11PlatformOptions Options { get; private set; }
public IntPtr OrphanedWindow { get; private set; }
@@ -96,17 +97,17 @@ namespace Avalonia.X11
if (options.UseGpu)
{
if (options.UseEGL)
- EglPlatformOpenGlInterface.TryInitialize();
+ EglPlatformGraphics.TryInitialize();
else
- GlxPlatformOpenGlInterface.TryInitialize(Info, Options.GlProfiles);
+ GlxPlatformGraphics.TryInitialize(Info, Options.GlProfiles);
}
- var gl = AvaloniaLocator.Current.GetService();
- if (gl != null)
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
-
+ var gl = AvaloniaLocator.Current.GetService();
+
if (options.UseCompositor)
Compositor = new Compositor(AvaloniaLocator.Current.GetService()!, gl);
+ else
+ RenderInterface = new(gl);
}
diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs
index ba6029b350..87899b11ed 100644
--- a/src/Avalonia.X11/X11Screens.cs
+++ b/src/Avalonia.X11/X11Screens.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Platform;
using static Avalonia.X11.XLib;
-using JetBrains.Annotations;
namespace Avalonia.X11
{
diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs
index 7e46606c36..26515762b4 100644
--- a/src/Avalonia.X11/X11Structs.cs
+++ b/src/Avalonia.X11/X11Structs.cs
@@ -1768,7 +1768,9 @@ namespace Avalonia.X11 {
}
[StructLayout(LayoutKind.Sequential)]
+#pragma warning disable CA1815 // Override equals and operator equals on value types
public unsafe struct XImage
+#pragma warning restore CA1815 // Override equals and operator equals on value types
{
public int width, height; /* size of image */
public int xoffset; /* number of pixels offset in X direction */
@@ -1799,7 +1801,7 @@ namespace Avalonia.X11 {
internal IntPtr green_mask;
internal IntPtr blue_mask;
internal int colormap_size;
- internal int bits_per_rgb;
+ internal int bits_per_rgb;
}
internal enum XIMFeedback
diff --git a/src/Avalonia.X11/X11Window.Ime.cs b/src/Avalonia.X11/X11Window.Ime.cs
index 257580a5ec..e6066c7964 100644
--- a/src/Avalonia.X11/X11Window.Ime.cs
+++ b/src/Avalonia.X11/X11Window.Ime.cs
@@ -7,7 +7,6 @@ using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Platform.Interop;
-using JetBrains.Annotations;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
@@ -206,7 +205,7 @@ namespace Avalonia.X11
// This class is used to attach the text value of the key to an asynchronously dispatched KeyDown event
class RawKeyEventArgsWithText : RawKeyEventArgs
{
- public RawKeyEventArgsWithText([NotNull] IKeyboardDevice device, ulong timestamp, [NotNull] IInputRoot root,
+ public RawKeyEventArgsWithText(IKeyboardDevice device, ulong timestamp, IInputRoot root,
RawKeyEventType type, Key key, RawInputModifiers modifiers, string text) :
base(device, timestamp, root, type, key, modifiers)
{
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 2f84e15b32..810a806c8a 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -57,6 +57,7 @@ namespace Avalonia.X11
private TransparencyHelper _transparencyHelper;
private RawEventGrouper _rawEventGrouper;
private bool _useRenderWindow = false;
+ private bool _usePositioningFlags = false;
enum XSyncState
{
@@ -74,7 +75,7 @@ namespace Avalonia.X11
_touch = new TouchDevice();
_keyboard = platform.KeyboardDevice;
- var glfeature = AvaloniaLocator.Current.GetService();
+ var glfeature = AvaloniaLocator.Current.GetService();
XSetWindowAttributes attr = new XSetWindowAttributes();
var valueMask = default(SetWindowValuemask);
@@ -96,13 +97,13 @@ namespace Avalonia.X11
// OpenGL seems to be do weird things to it's current window which breaks resize sometimes
_useRenderWindow = glfeature != null;
- var glx = glfeature as GlxPlatformOpenGlInterface;
+ var glx = glfeature as GlxPlatformGraphics;
if (glx != null)
visualInfo = *glx.Display.VisualInfo;
else if (glfeature == null)
visualInfo = _x11.TransparentVisualInfo;
- var egl = glfeature as EglPlatformOpenGlInterface;
+ var egl = glfeature as EglPlatformGraphics;
var visual = IntPtr.Zero;
var depth = 24;
@@ -176,11 +177,9 @@ namespace Avalonia.X11
if (egl != null)
surfaces.Insert(0,
- new EglGlPlatformSurface(egl,
- new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
+ new EglGlPlatformSurface(new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
if (glx != null)
- surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,
- new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
+ surfaces.Insert(0, new GlxGlPlatformSurface(new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
surfaces.Add(Handle);
@@ -300,7 +299,10 @@ namespace Avalonia.X11
min_height = min.Height
};
hints.height_inc = hints.width_inc = 1;
- var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc | XSizeHintsFlags.PPosition | XSizeHintsFlags.PSize;
+ var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc;
+ if (_usePositioningFlags)
+ flags |= XSizeHintsFlags.PPosition | XSizeHintsFlags.PSize;
+
// People might be passing double.MaxValue
if (max.Width < 100000 && max.Height < 100000)
{
@@ -356,7 +358,7 @@ namespace Avalonia.X11
public Action ScalingChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public Action WindowStateChanged { get; set; }
public Action TransparencyLevelChanged
@@ -391,12 +393,14 @@ namespace Avalonia.X11
return _platform.Options.UseDeferredRendering
? _platform.Options.UseCompositor
- ? new CompositingRenderer(root, this._platform.Compositor)
- : new DeferredRenderer(root, loop)
+ ? new CompositingRenderer(root, this._platform.Compositor, () => Surfaces)
+ : new DeferredRenderer(root, loop, () => _platform.RenderInterface.CreateRenderTarget(Surfaces), _platform.RenderInterface)
{
RenderOnlyOnRenderThread = true
}
- : (IRenderer)new X11ImmediateRendererProxy((Visual)root, loop);
+ : new X11ImmediateRendererProxy((Visual)root, loop,
+ () => _platform.RenderInterface.CreateRenderTarget(Surfaces),
+ _platform.RenderInterface);
}
void OnEvent(ref XEvent ev)
@@ -542,7 +546,7 @@ namespace Avalonia.X11
{
if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
{
- if (Closing?.Invoke() != true)
+ if (Closing?.Invoke(WindowCloseReason.WindowClosing) != true)
Dispose();
}
else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
@@ -958,6 +962,12 @@ namespace Avalonia.X11
get => _position ?? default;
set
{
+ if(!_usePositioningFlags)
+ {
+ _usePositioningFlags = true;
+ UpdateSizeHints(null);
+ }
+
var changes = new XWindowChanges
{
x = (int)value.X,
diff --git a/src/Browser/Avalonia.Browser/AvaloniaView.cs b/src/Browser/Avalonia.Browser/AvaloniaView.cs
index a407e1e4d8..abda618b0d 100644
--- a/src/Browser/Avalonia.Browser/AvaloniaView.cs
+++ b/src/Browser/Avalonia.Browser/AvaloniaView.cs
@@ -11,6 +11,7 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
+using Avalonia.Platform;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
using SkiaSharp;
@@ -106,7 +107,7 @@ namespace Avalonia.Browser
_dpi = DomHelper.ObserveDpi(OnDpiChanged);
- _useGL = skiaOptions?.CustomGpuFactory != null;
+ _useGL = AvaloniaLocator.Current.GetRequiredService() != null;
if (_useGL)
{
diff --git a/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs b/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs
index 7e91d29019..c2e54c7ed7 100644
--- a/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs
+++ b/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs
@@ -55,12 +55,6 @@ namespace Avalonia.Browser
private class Attachment : INativeControlHostControlTopLevelAttachment
{
- private const string InitializeWithChildHandleSymbol = "InitializeWithChildHandle";
- private const string AttachToSymbol = "AttachTo";
- private const string ShowInBoundsSymbol = "ShowInBounds";
- private const string HideWithSizeSymbol = "HideWithSize";
- private const string ReleaseChildSymbol = "ReleaseChild";
-
private JSObject? _native;
private BrowserNativeControlHost? _attachedTo;
diff --git a/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs
index ee4f6eca9b..b6c766b2a5 100644
--- a/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs
+++ b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs
@@ -48,7 +48,6 @@ public static class WebAppBuilder
{
return builder
.UseWindowingSubsystem(BrowserWindowingPlatform.Register)
- .UseSkia()
- .With(new SkiaOptions { CustomGpuFactory = () => new BrowserSkiaGpu() });
+ .UseSkia();
}
}
diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
index 69e2d27181..85725e4cf3 100644
--- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
+++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
@@ -166,7 +166,8 @@ namespace Avalonia.Browser
public IRenderer CreateRenderer(IRenderRoot root)
{
var loop = AvaloniaLocator.Current.GetRequiredService();
- return new CompositingRenderer(root, new Compositor(loop, null));
+ return new CompositingRenderer(root,
+ new Compositor(loop, AvaloniaLocator.Current.GetRequiredService()), () => Surfaces);
}
public void Invalidate(Rect rect)
diff --git a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs
index a96ead93cb..e56efdb4a8 100644
--- a/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs
+++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs
@@ -1,4 +1,7 @@
+using System;
using System.Collections.Generic;
+using System.Reactive.Disposables;
+using Avalonia.Platform;
using Avalonia.Skia;
namespace Avalonia.Browser.Skia
@@ -22,5 +25,28 @@ namespace Avalonia.Browser.Skia
{
return null;
}
+
+ public void Dispose()
+ {
+
+ }
+
+ public object? TryGetFeature(Type t) => null;
+
+ public bool IsLost => false;
+
+ public IDisposable EnsureCurrent()
+ {
+ return Disposable.Empty;
+ }
+ }
+
+ class BrowserSkiaGraphics : IPlatformGraphics
+ {
+ private BrowserSkiaGpu _skia = new();
+ public bool UsesSharedContext => true;
+ public IPlatformGraphicsContext CreateContext() => throw new NotSupportedException();
+
+ public IPlatformGraphicsContext GetSharedContext() => _skia;
}
}
diff --git a/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs b/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs
index e389ee98ea..df1a24fa0f 100644
--- a/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs
+++ b/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Browser
_onFirstRender = onFirstRender;
}
- public Rect Bounds => Rect.Empty;
+ public Rect Bounds => default;
public bool HasRendered => _hasRendered;
diff --git a/src/Browser/Avalonia.Browser/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs
index 6535e9534c..6493374a50 100644
--- a/src/Browser/Avalonia.Browser/WindowingPlatform.cs
+++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading;
+using Avalonia.Browser.Skia;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
@@ -43,6 +44,7 @@ namespace Avalonia.Browser
.Bind().ToConstant(new RenderLoop())
.Bind().ToConstant(ManualTriggerRenderTimer.Instance)
.Bind().ToConstant(instance)
+ .Bind().ToConstant(new BrowserSkiaGraphics())
.Bind().ToSingleton()
.Bind().ToSingleton();
}
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs b/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
index ce843952e7..3439f0edae 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/DrmOutputOptions.cs
@@ -1,6 +1,5 @@
using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Media;
-using JetBrains.Annotations;
namespace Avalonia.LinuxFramebuffer
{
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
index b64423ec10..ac54365f51 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
@@ -1,4 +1,4 @@
-using System;
+ using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
@@ -33,7 +33,7 @@ namespace Avalonia.LinuxFramebuffer
{
var factory = AvaloniaLocator.Current.GetService();
var renderLoop = AvaloniaLocator.Current.GetService();
- return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, LinuxFramebufferPlatform.Compositor);
+ return factory?.Create(root, renderLoop) ?? new CompositingRenderer(root, LinuxFramebufferPlatform.Compositor, () => Surfaces);
}
public void Dispose()
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
index 1e3c4bed48..686050e7c2 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading;
using Avalonia.Input;
using Avalonia.Input.Raw;
-using Avalonia.Threading;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
namespace Avalonia.LinuxFramebuffer.Input.EvDev
@@ -13,7 +12,6 @@ namespace Avalonia.LinuxFramebuffer.Input.EvDev
private readonly EvDevDeviceDescription[] _deviceDescriptions;
private readonly List _handlers = new List();
private int _epoll;
- private object _lock = new object();
private Action _onInput;
private IInputRoot _inputRoot;
private RawEventGroupingThreadingHelper _inputQueue;
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
index 77e8202fac..bff9ddc55c 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
@@ -11,7 +11,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
{
private IScreenInfoProvider _screen;
private IInputRoot _inputRoot;
- private readonly Queue _inputThreadActions = new Queue();
private TouchDevice _touch = new TouchDevice();
private const string LibInput = nameof(Avalonia.LinuxFramebuffer) + "/" + nameof(Avalonia.LinuxFramebuffer.Input) + "/" + nameof(LibInput);
private readonly RawEventGroupingThreadingHelper _inputQueue;
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
index d881a97af2..e2e9ec8fb3 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
@@ -17,7 +17,7 @@ using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
-using JetBrains.Annotations;
+#nullable enable
namespace Avalonia.LinuxFramebuffer
{
@@ -26,9 +26,9 @@ namespace Avalonia.LinuxFramebuffer
IOutputBackend _fb;
private static readonly Stopwatch St = Stopwatch.StartNew();
internal static uint Timestamp => (uint)St.ElapsedTicks;
- public static InternalPlatformThreadingInterface Threading;
+ public static InternalPlatformThreadingInterface? Threading;
- internal static Compositor Compositor { get; private set; }
+ internal static Compositor? Compositor { get; private set; }
LinuxFramebufferPlatform(IOutputBackend backend)
@@ -41,7 +41,7 @@ namespace Avalonia.LinuxFramebuffer
{
Threading = new InternalPlatformThreadingInterface();
if (_fb is IGlOutputBackend gl)
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl.PlatformOpenGlInterface);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl.PlatformGraphics);
var opts = AvaloniaLocator.Current.GetService() ?? new LinuxFramebufferPlatformOptions();
@@ -56,11 +56,11 @@ namespace Avalonia.LinuxFramebuffer
Compositor = new Compositor(
AvaloniaLocator.Current.GetRequiredService(),
- AvaloniaLocator.Current.GetService());
+ AvaloniaLocator.Current.GetService());
}
- internal static LinuxFramebufferLifetime Initialize(T builder, IOutputBackend outputBackend, IInputBackend inputBackend) where T : AppBuilderBase, new()
+ internal static LinuxFramebufferLifetime Initialize(T builder, IOutputBackend outputBackend, IInputBackend? inputBackend) where T : AppBuilderBase, new()
{
var platform = new LinuxFramebufferPlatform(outputBackend);
builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev");
@@ -71,8 +71,8 @@ namespace Avalonia.LinuxFramebuffer
class LinuxFramebufferLifetime : IControlledApplicationLifetime, ISingleViewApplicationLifetime
{
private readonly IOutputBackend _fb;
- [CanBeNull] private readonly IInputBackend _inputBackend;
- private TopLevel _topLevel;
+ private readonly IInputBackend? _inputBackend;
+ private TopLevel? _topLevel;
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public CancellationToken Token => _cts.Token;
@@ -81,15 +81,15 @@ namespace Avalonia.LinuxFramebuffer
_fb = fb;
}
- public LinuxFramebufferLifetime(IOutputBackend fb, IInputBackend input)
+ public LinuxFramebufferLifetime(IOutputBackend fb, IInputBackend? input)
{
_fb = fb;
_inputBackend = input;
}
- public Control MainView
+ public Control? MainView
{
- get => (Control)_topLevel?.Content;
+ get => (Control?)_topLevel?.Content;
set
{
if (_topLevel == null)
@@ -119,8 +119,8 @@ namespace Avalonia.LinuxFramebuffer
}
public int ExitCode { get; private set; }
- public event EventHandler Startup;
- public event EventHandler Exit;
+ public event EventHandler? Startup;
+ public event EventHandler? Exit;
public void Start(string[] args)
{
@@ -140,19 +140,19 @@ namespace Avalonia.LinuxFramebuffer
public static class LinuxFramebufferPlatformExtensions
{
- public static int StartLinuxFbDev(this T builder, string[] args, string fbdev = null, double scaling = 1, IInputBackend inputBackend = default)
+ public static int StartLinuxFbDev(this T builder, string[] args, string? fbdev = null, double scaling = 1, IInputBackend? inputBackend = default)
where T : AppBuilderBase, new() =>
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: null) { Scaling = scaling }, inputBackend);
- public static int StartLinuxFbDev(this T builder, string[] args, string fbdev, PixelFormat? format, double scaling, IInputBackend inputBackend = default)
+ public static int StartLinuxFbDev(this T builder, string[] args, string fbdev, PixelFormat? format, double scaling, IInputBackend? inputBackend = default)
where T : AppBuilderBase, new() =>
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: format) { Scaling = scaling }, inputBackend);
- public static int StartLinuxDrm(this T builder, string[] args, string card = null, double scaling = 1, IInputBackend inputBackend = default)
+ public static int StartLinuxDrm(this T builder, string[] args, string? card = null, double scaling = 1, IInputBackend? inputBackend = default)
where T : AppBuilderBase, new() => StartLinuxDirect(builder, args, new DrmOutput(card) { Scaling = scaling }, inputBackend);
- public static int StartLinuxDrm(this T builder, string[] args, string card = null, bool connectorsForceProbe = false, [CanBeNull] DrmOutputOptions options = null, IInputBackend inputBackend = default)
+ public static int StartLinuxDrm(this T builder, string[] args, string? card = null, bool connectorsForceProbe = false, DrmOutputOptions? options = null, IInputBackend? inputBackend = default)
where T : AppBuilderBase, new() => StartLinuxDirect(builder, args, new DrmOutput(card, connectorsForceProbe, options), inputBackend);
- public static int StartLinuxDirect(this T builder, string[] args, IOutputBackend outputBackend, IInputBackend inputBackend = default)
+ public static int StartLinuxDirect(this T builder, string[] args, IOutputBackend outputBackend, IInputBackend? inputBackend = default)
where T : AppBuilderBase, new()
{
var lifetime = LinuxFramebufferPlatform.Initialize(builder, outputBackend, inputBackend);
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
index a4d7d8b1eb..d61dcd4f91 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
@@ -6,8 +6,8 @@ using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
+using Avalonia.Platform;
using Avalonia.Platform.Interop;
-using JetBrains.Annotations;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
using static Avalonia.LinuxFramebuffer.Output.LibDrm;
using static Avalonia.LinuxFramebuffer.Output.LibDrm.GbmColorFormats;
@@ -25,10 +25,22 @@ namespace Avalonia.LinuxFramebuffer.Output
get => _outputOptions.Scaling;
set => _outputOptions.Scaling = value;
}
- public IGlContext PrimaryContext => _deferredContext;
- private EglPlatformOpenGlInterface _platformGl;
- public IPlatformOpenGlInterface PlatformOpenGlInterface => _platformGl;
+ class SharedContextGraphics : IPlatformGraphics
+ {
+ private readonly IPlatformGraphicsContext _context;
+
+ public SharedContextGraphics(IPlatformGraphicsContext context)
+ {
+ _context = context;
+ }
+ public bool UsesSharedContext => true;
+ public IPlatformGraphicsContext CreateContext() => throw new NotSupportedException();
+
+ public IPlatformGraphicsContext GetSharedContext() => _context;
+ }
+
+ public IPlatformGraphics PlatformGraphics { get; private set; }
public DrmOutput(DrmCard card, DrmResources resources, DrmConnector connector, DrmModeInfo modeInfo,
DrmOutputOptions? options = null)
@@ -37,7 +49,7 @@ namespace Avalonia.LinuxFramebuffer.Output
_outputOptions = options;
Init(card, resources, connector, modeInfo);
}
- public DrmOutput(string path = null, bool connectorsForceProbe = false, [CanBeNull] DrmOutputOptions options = null)
+ public DrmOutput(string path = null, bool connectorsForceProbe = false, DrmOutputOptions? options = null)
{
if(options != null)
_outputOptions = options;
@@ -161,11 +173,22 @@ namespace Avalonia.LinuxFramebuffer.Output
if(_gbmTargetSurface == IntPtr.Zero)
throw new InvalidOperationException("Unable to create GBM surface");
- _eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), false, 0x31D7, device, null);
- _platformGl = new EglPlatformOpenGlInterface(_eglDisplay);
- _eglSurface = _platformGl.CreateWindowSurface(_gbmTargetSurface);
+ _eglDisplay = new EglDisplay(
+ new EglDisplayCreationOptions
+ {
+ Egl = new EglInterface(eglGetProcAddress),
+ PlatformType = 0x31D7,
+ PlatformDisplay = device,
+ SupportsMultipleContexts = true,
+ SupportsContextSharing = true
+ });
+
+ var surface = _eglDisplay.EglInterface.CreateWindowSurface(_eglDisplay.Handle, _eglDisplay.Config, _gbmTargetSurface, new[] { EglConsts.EGL_NONE, EglConsts.EGL_NONE });
+
+ _eglSurface = new EglSurface(_eglDisplay, surface);
- _deferredContext = _platformGl.PrimaryEglContext;
+ _deferredContext = _eglDisplay.CreateContext(null);
+ PlatformGraphics = new SharedContextGraphics(_deferredContext);
var initialBufferSwappingColorR = _outputOptions.InitialBufferSwappingColor.R / 255.0f;
var initialBufferSwappingColorG = _outputOptions.InitialBufferSwappingColor.G / 255.0f;
@@ -206,11 +229,17 @@ namespace Avalonia.LinuxFramebuffer.Output
}
- public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() => new RenderTarget(this);
+
+
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
- return new RenderTarget(this);
+ if (context != _deferredContext)
+ throw new InvalidOperationException(
+ "This platform backend can only create render targets for its primary context");
+ return CreateGlRenderTarget();
}
-
+
class RenderTarget : IGlPlatformSurfaceRenderTarget
{
private readonly DrmOutput _parent;
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
index 7bc73d590c..e415b1782e 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
@@ -1,9 +1,10 @@
using Avalonia.OpenGL;
+using Avalonia.Platform;
namespace Avalonia.LinuxFramebuffer.Output
{
public interface IGlOutputBackend : IOutputBackend
{
- public IPlatformOpenGlInterface PlatformOpenGlInterface { get; }
+ public IPlatformGraphics PlatformGraphics { get; }
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
index b4c951fc5e..6f6420f66d 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
@@ -56,8 +56,8 @@ namespace Avalonia.Markup.Xaml
///
/// Collection of documents.
/// Xaml loader configuration.
- /// The loaded objects per each input document.
- public static IReadOnlyList LoadGroup(IReadOnlyCollection documents, RuntimeXamlLoaderConfiguration? configuration = null)
+ /// The loaded objects per each input document. If document was removed, the element by index is null.
+ public static IReadOnlyList LoadGroup(IReadOnlyCollection documents, RuntimeXamlLoaderConfiguration? configuration = null)
=> AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration());
///
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index b8350c3f11..aaaee39b0d 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -85,6 +85,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
GroupTransformers = new()
{
+ new XamlMergeResourceGroupTransformer(),
new AvaloniaXamlIncludeTransformer()
};
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
index 32bf37431f..eeb5293325 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
@@ -18,7 +18,7 @@ internal class AstGroupTransformationContext : AstTransformationContext
public IXamlDocumentResource CurrentDocument { get; set; }
public IReadOnlyCollection Documents { get; }
-
+
public new IXamlAstNode ParseError(string message, IXamlAstNode node) =>
Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node));
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
index 1bb05e238c..afc7b569a8 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
@@ -15,6 +15,7 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
+#nullable enable
internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{
public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
@@ -34,40 +35,26 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{
throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined");
}
-
+
if (valueNode.Manipulation is not XamlObjectInitializationNode
{
Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty
})
{
- return context.ParseError($"Source property must be set on the \"{nodeTypeName}\" node.", node);
+ throw new XamlDocumentParseException(context.CurrentDocument,
+ $"Source property must be set on the \"{nodeTypeName}\" node.", valueNode);
}
- // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
- if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode
- || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
- || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
- || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })
+ var (assetPathUri, sourceUriNode) = ResolveSourceFromXamlInclude(context, nodeTypeName, sourceProperty, false);
+ if (assetPathUri is null)
{
- // TODO: make it a compiler warning
- // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
return node;
}
-
- var uriPath = new Uri(originalAssetPath, (UriKind)uriKind);
- if (!uriPath.IsAbsoluteUri)
+ else
{
- var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null.");
- uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath);
- }
- else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase))
- {
- return context.ParseError(
- $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.",
- sourceUriNode, node);
+ sourceUriNode ??= valueNode;
}
- var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri);
var assetPath = assetPathUri.Replace("avares://", "");
var assemblyNameSeparator = assetPath.IndexOf('/');
var assembly = assetPath.Substring(0, assemblyNameSeparator);
@@ -119,7 +106,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
$"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.",
sourceUriNode, node);
}
-
+
private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li,
IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly)
{
@@ -151,7 +138,49 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
new[] { new NewServiceProviderNode(sp, li) });
}
- internal class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack,
+ internal static (string?, IXamlAstNode?) ResolveSourceFromXamlInclude(
+ AstGroupTransformationContext context, string nodeTypeName, XamlPropertyAssignmentNode sourceProperty,
+ bool strictSourceValueType)
+ {
+ // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
+ if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode
+ || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
+ || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
+ || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })
+ {
+ // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
+ var anyPropValue = sourceProperty.Values.FirstOrDefault();
+ if (strictSourceValueType)
+ {
+ context.Error(anyPropValue,
+ new XamlDocumentParseException(context.CurrentDocument,
+ $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", anyPropValue));
+ }
+ else
+ {
+ // TODO: make it a compiler warning
+ }
+ return (null, anyPropValue);
+ }
+
+ var uriPath = new Uri(originalAssetPath, (UriKind)uriKind);
+ if (!uriPath.IsAbsoluteUri)
+ {
+ var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null.");
+ uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath);
+ }
+ else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase))
+ {
+ context.Error(sourceUriNode,
+ new XamlDocumentParseException(context.CurrentDocument,
+ $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", sourceUriNode));
+ return (null, sourceUriNode);
+ }
+
+ return (Uri.UnescapeDataString(uriPath.AbsoluteUri), sourceUriNode);
+ }
+
+ private class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack,
IXamlAstEmitableNode
{
public NewServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo) : base(lineInfo)
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
new file mode 100644
index 0000000000..8c83c74248
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX.Ast;
+using XamlX.IL.Emitters;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
+#nullable enable
+
+internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
+{
+ public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
+ {
+ var resourceDictionaryType = context.GetAvaloniaTypes().ResourceDictionary;
+ if (node is not XamlObjectInitializationNode resourceDictionaryNode
+ || resourceDictionaryNode.Type != resourceDictionaryType
+ || resourceDictionaryNode.Manipulation is not XamlManipulationGroupNode resourceDictionaryManipulation)
+ {
+ return node;
+ }
+
+ var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
+ var mergeSourceNodes = new List();
+ var hasAnyNonMergedResource = false;
+ foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
+ {
+ void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
+ {
+ if (assignmentNode.Property.Name == "MergedDictionaries"
+ && assignmentNode.Values.FirstOrDefault() is XamlValueWithManipulationNode valueNode)
+ {
+ if (valueNode.Type.GetClrType() == mergeResourceIncludeType)
+ {
+ if (valueNode.Manipulation is XamlObjectInitializationNode objectInitialization
+ && objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode)
+ {
+ parent.Children.Remove(assignmentNode);
+ mergeSourceNodes.Add(sourceAssignmentNode);
+ }
+ else
+ {
+ throw new XamlDocumentParseException(context.CurrentDocument,
+ "Invalid MergeResourceInclude node found. Make sure that Source property is set.",
+ valueNode);
+ }
+ }
+ else
+ {
+ hasAnyNonMergedResource = true;
+ }
+
+ if (hasAnyNonMergedResource && mergeSourceNodes.Any())
+ {
+ throw new XamlDocumentParseException(context.CurrentDocument,
+ "Mix of MergeResourceInclude and other dictionaries inside of the ResourceDictionary.MergedDictionaries is not allowed",
+ valueNode);
+ }
+ }
+ }
+
+ if (manipulationNode is XamlPropertyAssignmentNode singleValueAssignment)
+ {
+ ProcessXamlPropertyAssignmentNode(resourceDictionaryManipulation, singleValueAssignment);
+ }
+ else if (manipulationNode is XamlManipulationGroupNode groupNodeValues)
+ {
+ foreach (var groupNodeValue in groupNodeValues.Children.OfType().ToArray())
+ {
+ ProcessXamlPropertyAssignmentNode(groupNodeValues, groupNodeValue);
+ }
+ }
+ }
+
+ var manipulationGroup = new XamlManipulationGroupNode(node, new List());
+ foreach (var sourceNode in mergeSourceNodes)
+ {
+ var (originalAssetPath, propertyNode) =
+ AvaloniaXamlIncludeTransformer.ResolveSourceFromXamlInclude(context, "MergeResourceInclude", sourceNode, true);
+ if (originalAssetPath is null)
+ {
+ return node;
+ }
+
+ var targetDocument = context.Documents.FirstOrDefault(d =>
+ string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase))
+ ?.XamlDocument.Root as XamlValueWithManipulationNode;
+ if (targetDocument is null)
+ {
+ return context.ParseError(
+ $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
+ }
+
+ var singleRootObject = ((XamlManipulationGroupNode)targetDocument.Manipulation)
+ .Children.OfType().Single();
+ if (singleRootObject.Type != resourceDictionaryType)
+ {
+ return context.ParseError(
+ $"MergeResourceInclude can only include another ResourceDictionary", propertyNode, node);
+ }
+
+ manipulationGroup.Children.Add(singleRootObject.Manipulation);
+ }
+
+ if (manipulationGroup.Children.Any())
+ {
+ // MergedDictionaries are read first, so we need ot inject our merged values in the beginning.
+ resourceDictionaryManipulation.Children.Insert(0, manipulationGroup);
+ }
+
+ return node;
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index 87a037c16a..5753a1008c 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -104,6 +104,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType IStyle { get; }
public IXamlType StyleInclude { get; }
public IXamlType ResourceInclude { get; }
+ public IXamlType MergeResourceInclude { get; }
public IXamlType IResourceDictionary { get; }
public IXamlType ResourceDictionary { get; }
public IXamlMethod ResourceDictionaryDeferredAdd { get; }
@@ -236,6 +237,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle");
StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude");
ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude");
+ MergeResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.MergeResourceInclude");
IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary");
ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
index d031a6086b..0532287a67 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
@@ -18,4 +18,9 @@ internal class XamlDocumentParseException : XamlParseException
{
FilePath = path;
}
+
+ public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo)
+ : this(document.FileSource?.FilePath, message, lineInfo)
+ {
+ }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index c59542c0f3..267bd2520e 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -43,6 +43,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
index 3084964d44..4478a79d27 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
@@ -44,9 +44,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public CommandAccessor(WeakReference reference, Action execute, Func canExecute, ISet dependsOnProperties)
{
- Contract.Requires(reference != null);
-
- _reference = reference;
+ _reference = reference ?? throw new ArgumentNullException(nameof(reference));
_dependsOnProperties = dependsOnProperties;
_command = new Command(reference, execute, canExecute);
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
index ef11b06369..81f1224650 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
@@ -28,11 +28,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public AvaloniaPropertyAccessor(WeakReference reference, AvaloniaProperty property)
{
- Contract.Requires(reference != null);
- Contract.Requires(property != null);
-
- _reference = reference;
- _property = property;
+ _reference = reference ?? throw new ArgumentNullException(nameof(reference));;
+ _property = property ?? throw new ArgumentNullException(nameof(property));;
}
public AvaloniaObject Instance
@@ -77,11 +74,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public InpcPropertyAccessor(WeakReference reference, IPropertyInfo property)
{
- Contract.Requires(reference != null);
- Contract.Requires(property != null);
-
- _reference = reference;
- _property = property;
+ _reference = reference ?? throw new ArgumentNullException(nameof(reference));
+ _property = property ?? throw new ArgumentNullException(nameof(property));
}
public override Type PropertyType => _property.PropertyType;
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
new file mode 100644
index 0000000000..d231a1d221
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Avalonia.Controls;
+
+#nullable enable
+
+namespace Avalonia.Markup.Xaml.Styling;
+
+///
+/// Loads a resource dictionary from a specified URL.
+///
+///
+/// If used from the XAML code, it is merged into the parent dictionary in the compile time.
+/// When used in runtime, this type behaves like .
+///
+[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
+public class MergeResourceInclude : ResourceInclude
+{
+ public MergeResourceInclude(Uri? baseUri) : base(baseUri)
+ {
+ }
+
+ public MergeResourceInclude(IServiceProvider serviceProvider) : base(serviceProvider)
+ {
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
index 3d8a61364a..595b37f7d1 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
@@ -9,6 +9,10 @@ namespace Avalonia.Markup.Xaml.Styling
///
/// Loads a resource dictionary from a specified URL.
///
+ ///
+ /// If used from the XAML code, it is replaced with direct resource dictionary reference.
+ /// When used in runtime, this type might be unsafe with trimming and AOT.
+ ///
[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
public class ResourceInclude : IResourceProvider
{
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
index bdde6e725c..b87aa64297 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
@@ -11,6 +11,10 @@ namespace Avalonia.Markup.Xaml.Styling
///
/// Includes a style from a URL.
///
+ ///
+ /// If used from the XAML code, it is replaced with direct style reference.
+ /// When used in runtime, this type might be unsafe with trimming and AOT.
+ ///
[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
public class StyleInclude : IStyle, IResourceProvider
{
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index f18330ba8a..48e8c761eb 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -10,7 +10,6 @@ using Avalonia.Rendering.SceneGraph;
using Avalonia.Rendering.Utilities;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
-using JetBrains.Annotations;
using SkiaSharp;
namespace Avalonia.Skia
@@ -362,7 +361,7 @@ namespace Avalonia.Skia
foreach (var boxShadow in boxShadows)
{
- if (!boxShadow.IsEmpty && !boxShadow.IsInset)
+ if (!boxShadow.IsDefault && !boxShadow.IsInset)
{
using (var shadow = BoxShadowFilter.Create(_boxShadowPaint, boxShadow, _currentOpacity))
{
@@ -418,7 +417,7 @@ namespace Avalonia.Skia
foreach (var boxShadow in boxShadows)
{
- if (!boxShadow.IsEmpty && boxShadow.IsInset)
+ if (!boxShadow.IsDefault && boxShadow.IsInset)
{
using (var shadow = BoxShadowFilter.Create(_boxShadowPaint, boxShadow, _currentOpacity))
{
@@ -675,13 +674,14 @@ namespace Avalonia.Skia
}
}
- [CanBeNull]
- public object GetFeature(Type t)
+#nullable enable
+ public object? GetFeature(Type t)
{
if (t == typeof(ISkiaSharpApiLeaseFeature))
return new SkiaLeaseFeature(this);
return null;
}
+#nullable restore
///
/// Configure paint wrapper for using gradient brush.
@@ -1137,11 +1137,14 @@ namespace Avalonia.Skia
if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)
{
var srcDashes = pen.DashStyle.Dashes;
- var dashesArray = new float[srcDashes.Count];
- for (var i = 0; i < srcDashes.Count; ++i)
+ var count = srcDashes.Count % 2 == 0 ? srcDashes.Count : srcDashes.Count * 2;
+
+ var dashesArray = new float[count];
+
+ for (var i = 0; i < count; ++i)
{
- dashesArray[i] = (float) srcDashes[i] * paint.StrokeWidth;
+ dashesArray[i] = (float) srcDashes[i % srcDashes.Count] * paint.StrokeWidth;
}
var offset = (float)(pen.DashStyle.Offset * pen.Thickness);
diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
index 9dbfd0f8d9..1ae47c8b7a 100644
--- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
@@ -61,6 +61,8 @@ namespace Avalonia.Skia
return new DrawingContextImpl(createInfo, _preFramebufferCopyHandler, canvas, framebuffer);
}
+ public bool IsCorrupted => false;
+
///
/// Check if two images info are compatible.
///
diff --git a/src/Skia/Avalonia.Skia/GeometryImpl.cs b/src/Skia/Avalonia.Skia/GeometryImpl.cs
index 4037cc4a35..1141763097 100644
--- a/src/Skia/Avalonia.Skia/GeometryImpl.cs
+++ b/src/Skia/Avalonia.Skia/GeometryImpl.cs
@@ -244,7 +244,7 @@ namespace Avalonia.Skia
public void Invalidate()
{
CachedStrokePath?.Dispose();
- CachedGeometryRenderBounds = Rect.Empty;
+ CachedGeometryRenderBounds = default;
_cachedStrokeWidth = default(float);
}
}
diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
index bdc3d075cf..484fd9f219 100644
--- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs
@@ -1,8 +1,8 @@
using System;
using Avalonia.Metadata;
using Avalonia.Platform;
-using JetBrains.Annotations;
using SkiaSharp;
+#nullable enable
namespace Avalonia.Skia
{
@@ -10,7 +10,7 @@ namespace Avalonia.Skia
[Unstable]
public class GlyphRunImpl : IGlyphRunImpl
{
- public GlyphRunImpl([NotNull] SKTextBlob textBlob)
+ public GlyphRunImpl(SKTextBlob textBlob)
{
TextBlob = textBlob ?? throw new ArgumentNullException (nameof (textBlob));
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
index 4a7d4d4ac8..b064445a0b 100644
--- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
+using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia
@@ -8,7 +10,7 @@ namespace Avalonia.Skia
///
/// Custom Skia gpu instance.
///
- public interface ISkiaGpu
+ public interface ISkiaGpu : IPlatformGraphicsContext
{
///
/// Attempts to create custom render target from given surfaces.
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
index 5cc338a469..e19379df09 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
@@ -1,11 +1,13 @@
using System;
using Avalonia.OpenGL;
+using Avalonia.Platform;
using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
- public class FboSkiaSurface : ISkiaSurface
+ internal class FboSkiaSurface : ISkiaSurface
{
+ private readonly GlSkiaGpu _gpu;
private readonly GRContext _grContext;
private readonly IGlContext _glContext;
private readonly PixelSize _pixelSize;
@@ -14,8 +16,9 @@ namespace Avalonia.Skia
private int _texture;
private static readonly bool[] TrueFalse = new[] { true, false };
- public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin)
+ public FboSkiaSurface(GlSkiaGpu gpu, GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin)
{
+ _gpu = gpu;
_grContext = grContext;
_glContext = glContext;
_pixelSize = pixelSize;
@@ -93,19 +96,33 @@ namespace Avalonia.Skia
public void Dispose()
{
- using (_glContext.EnsureCurrent())
+ try
{
- Surface?.Dispose();
- Surface = null;
- var gl = _glContext.GlInterface;
- if (_fbo != 0)
+ using (_glContext.EnsureCurrent())
{
- gl.DeleteFramebuffer(_fbo);
- gl.DeleteTexture(_texture);
- gl.DeleteRenderbuffer(_depthStencil);
- _fbo = _texture = _depthStencil = 0;
+ Surface?.Dispose();
+ Surface = null;
+ var gl = _glContext.GlInterface;
+ if (_fbo != 0)
+ {
+ gl.DeleteFramebuffer(_fbo);
+ gl.DeleteTexture(_texture);
+ gl.DeleteRenderbuffer(_depthStencil);
+ }
}
}
+ catch (PlatformGraphicsContextLostException)
+ {
+ if (Surface != null)
+ // We need to dispose SKSurface _after_ GRContext.Abandon was called,
+ // otherwise it will try to do OpenGL calls without a proper context
+ _gpu.AddPostDispose(Surface.Dispose);
+ Surface = null;
+ }
+ finally
+ {
+ _fbo = _texture = _depthStencil = 0;
+ }
}
public SKSurface Surface { get; private set; }
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
index 476ecc4a39..5bf1272c2f 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
@@ -14,10 +14,11 @@ namespace Avalonia.Skia
private readonly GRContext _grContext;
private IGlPlatformSurfaceRenderTarget _surface;
- public GlRenderTarget(GRContext grContext, IGlPlatformSurface glSurface)
+ public GlRenderTarget(GRContext grContext, IGlContext glContext, IGlPlatformSurface glSurface)
{
_grContext = grContext;
- _surface = glSurface.CreateGlRenderTarget();
+ using (glContext.EnsureCurrent())
+ _surface = glSurface.CreateGlRenderTarget(glContext);
}
public void Dispose() => _surface.Dispose();
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
index 08e0fbd808..cdba3b9ea2 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
@@ -5,21 +5,22 @@ using Avalonia.Logging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.OpenGL.Surfaces;
+using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia
{
- class GlSkiaGpu : IOpenGlAwareSkiaGpu
+ class GlSkiaGpu : IOpenGlAwareSkiaGpu, IOpenGlTextureSharingRenderInterfaceContextFeature
{
private GRContext _grContext;
private IGlContext _glContext;
+ private List _postDisposeCallbacks = new();
private bool? _canCreateSurfaces;
- public GlSkiaGpu(IPlatformOpenGlInterface openGl, long? maxResourceBytes)
+ public GlSkiaGpu(IGlContext context, long? maxResourceBytes)
{
- var context = openGl.PrimaryContext;
_glContext = context;
- using (context.MakeCurrent())
+ using (_glContext.EnsureCurrent())
{
using (var iface = context.Version.Type == GlProfileType.OpenGL ?
GRGlInterface.CreateOpenGl(proc => context.GlInterface.GetProcAddress(proc)) :
@@ -34,13 +35,34 @@ namespace Avalonia.Skia
}
}
+ class SurfaceWrapper : IGlPlatformSurface
+ {
+ private readonly object _surface;
+
+ public SurfaceWrapper( object surface)
+ {
+ _surface = surface;
+ }
+
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
+ {
+ var feature = context.TryGetFeature()!;
+ return feature.CreateRenderTarget(context, _surface);
+ }
+ }
+
public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable surfaces)
{
+ var customRenderTargetFactory = _glContext.TryGetFeature();
foreach (var surface in surfaces)
{
+ if (customRenderTargetFactory?.CanRenderToSurface(_glContext, surface) == true)
+ {
+ return new GlRenderTarget(_grContext, _glContext, new SurfaceWrapper(surface));
+ }
if (surface is IGlPlatformSurface glSurface)
{
- return new GlRenderTarget(_grContext, glSurface);
+ return new GlRenderTarget(_grContext, _glContext, glSurface);
}
}
@@ -62,7 +84,8 @@ namespace Avalonia.Skia
return null;
try
{
- var surface = new FboSkiaSurface(_grContext, _glContext, size, session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft);
+ var surface = new FboSkiaSurface(this, _grContext, _glContext, size,
+ session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft);
_canCreateSurfaces = true;
return surface;
}
@@ -75,6 +98,40 @@ namespace Avalonia.Skia
}
}
+ public bool CanCreateSharedContext => _glContext.CanCreateSharedContext;
+
+ public IGlContext CreateSharedContext(IEnumerable preferredVersions = null) =>
+ _glContext.CreateSharedContext(preferredVersions);
+
public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);
+
+ public void Dispose()
+ {
+ if (_glContext.IsLost)
+ _grContext.AbandonContext();
+ else
+ _grContext.AbandonContext(true);
+ _grContext.Dispose();
+
+ lock(_postDisposeCallbacks)
+ foreach (var cb in _postDisposeCallbacks)
+ cb();
+ }
+
+ public bool IsLost => _glContext.IsLost;
+ public IDisposable EnsureCurrent() => _glContext.EnsureCurrent();
+
+ public object TryGetFeature(Type featureType)
+ {
+ if (featureType == typeof(IOpenGlTextureSharingRenderInterfaceContextFeature))
+ return this;
+ return null;
+ }
+
+ public void AddPostDispose(Action dispose)
+ {
+ lock (_postDisposeCallbacks)
+ _postDisposeCallbacks.Add(dispose);
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
index 5e02500bf0..d8bff7cfc8 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
@@ -109,7 +109,9 @@ namespace Avalonia.Skia
using (_context.EnsureCurrent())
{
var glVersion = _context.Version;
- InternalFormat = glVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8;
+ InternalFormat = glVersion.Type == GlProfileType.OpenGLES && glVersion.Major == 2
+ ? GL_RGBA
+ : GL_RGBA8;
_context.GlInterface.GetIntegerv(GL_FRAMEBUFFER_BINDING, out _fbo);
if (_fbo == 0)
@@ -145,7 +147,7 @@ namespace Avalonia.Skia
public void Present()
{
- using (_context.MakeCurrent())
+ using (_context.EnsureCurrent())
{
if (_disposed)
throw new ObjectDisposedException(nameof(SharedOpenGlBitmapAttachment));
diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
index 6626546c0c..6b4a7a3409 100644
--- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Skia
///
/// Adapts to be used within our rendering pipeline.
///
- internal class SkiaGpuRenderTarget : IRenderTargetWithCorruptionInfo
+ internal class SkiaGpuRenderTarget : IRenderTarget
{
private readonly ISkiaGpu _skiaGpu;
private readonly ISkiaGpuRenderTarget _renderTarget;
diff --git a/src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs b/src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs
index b4dd754822..4cb1430a3b 100644
--- a/src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs
+++ b/src/Skia/Avalonia.Skia/Helpers/ImageSavingHelper.cs
@@ -34,6 +34,7 @@ namespace Avalonia.Skia.Helpers
/// Save Skia image to a stream.
///
/// Image to save
+ /// The output stream to save the image.
///
/// The optional quality for PNG compression.
/// The quality value is interpreted from 0 - 100. If quality is null
diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
index f34e25299c..b202b60cdf 100644
--- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
+++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
@@ -18,26 +18,28 @@ namespace Avalonia.Skia
///
/// Skia platform render interface.
///
- internal class PlatformRenderInterface : IPlatformRenderInterface, IOpenGlAwarePlatformRenderInterface
+ internal class PlatformRenderInterface : IPlatformRenderInterface
{
- private readonly ISkiaGpu _skiaGpu;
+ private readonly long? _maxResourceBytes;
- public PlatformRenderInterface(ISkiaGpu skiaGpu, long? maxResourceBytes = null)
+ public PlatformRenderInterface(long? maxResourceBytes = null)
{
+ _maxResourceBytes = maxResourceBytes;
DefaultPixelFormat = SKImageInfo.PlatformColorType.ToPixelFormat();
+ }
- if (skiaGpu != null)
- {
- _skiaGpu = skiaGpu;
- return;
- }
- var gl = AvaloniaLocator.Current.GetService();
- if (gl != null)
- _skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
+ public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext)
+ {
+ if (graphicsContext == null)
+ return new SkiaContext(null);
+ if (graphicsContext is ISkiaGpu skiaGpu)
+ return new SkiaContext(skiaGpu);
+ if (graphicsContext is IGlContext gl)
+ return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes));
+ throw new ArgumentException("Graphics context of type is not supported");
}
-
public bool SupportsIndividualRoundRects => true;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
@@ -205,43 +207,12 @@ namespace Avalonia.Skia
return new SurfaceRenderTarget(createInfo);
}
- ///
- public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
- {
- if (!(surfaces is IList))
- surfaces = surfaces.ToList();
- var gpuRenderTarget = _skiaGpu?.TryCreateRenderTarget(surfaces);
- if (gpuRenderTarget != null)
- {
- return new SkiaGpuRenderTarget(_skiaGpu, gpuRenderTarget);
- }
-
- foreach (var surface in surfaces)
- {
- if (surface is IFramebufferPlatformSurface framebufferSurface)
- return new FramebufferRenderTarget(framebufferSurface);
- }
-
- throw new NotSupportedException(
- "Don't know how to create a Skia render target from any of provided surfaces");
- }
-
///
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
- public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
- {
- if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
- return glAware.CreateOpenGlBitmap(size, dpi);
- if (_skiaGpu == null)
- throw new PlatformNotSupportedException("GPU acceleration is not available");
- throw new PlatformNotSupportedException(
- "Current GPU acceleration backend does not support OpenGL integration");
- }
-
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphIndices,
IReadOnlyList glyphAdvances, IReadOnlyList glyphOffsets)
{
diff --git a/src/Skia/Avalonia.Skia/SkiaBackendContext.cs b/src/Skia/Avalonia.Skia/SkiaBackendContext.cs
new file mode 100644
index 0000000000..4949f4a50d
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/SkiaBackendContext.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Platform;
+
+namespace Avalonia.Skia;
+
+internal class SkiaContext : IPlatformRenderInterfaceContext
+{
+ private ISkiaGpu _gpu;
+
+ public SkiaContext(ISkiaGpu gpu)
+ {
+ _gpu = gpu;
+ }
+
+ public void Dispose()
+ {
+ _gpu?.Dispose();
+ _gpu = null;
+ }
+
+ ///
+ public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
+ {
+ if (!(surfaces is IList))
+ surfaces = surfaces.ToList();
+ var gpuRenderTarget = _gpu?.TryCreateRenderTarget(surfaces);
+ if (gpuRenderTarget != null)
+ {
+ return new SkiaGpuRenderTarget(_gpu, gpuRenderTarget);
+ }
+
+ foreach (var surface in surfaces)
+ {
+ if (surface is IFramebufferPlatformSurface framebufferSurface)
+ return new FramebufferRenderTarget(framebufferSurface);
+ }
+
+ throw new NotSupportedException(
+ "Don't know how to create a Skia render target from any of provided surfaces");
+ }
+
+ public object TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType);
+}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs
index 493263677d..b3c3056a58 100644
--- a/src/Skia/Avalonia.Skia/SkiaOptions.cs
+++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs
@@ -8,11 +8,6 @@ namespace Avalonia
///
public class SkiaOptions
{
- ///
- /// Custom gpu factory to use. Can be used to customize behavior of Skia renderer.
- ///
- public Func CustomGpuFactory { get; set; }
-
///
/// The maximum number of bytes for video memory to store textures and resources.
///
diff --git a/src/Skia/Avalonia.Skia/SkiaPlatform.cs b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
index 9a5725e06f..27f2631db8 100644
--- a/src/Skia/Avalonia.Skia/SkiaPlatform.cs
+++ b/src/Skia/Avalonia.Skia/SkiaPlatform.cs
@@ -17,8 +17,7 @@ namespace Avalonia.Skia
public static void Initialize(SkiaOptions options)
{
- var customGpu = options.CustomGpuFactory?.Invoke();
- var renderInterface = new PlatformRenderInterface(customGpu, options.MaxGpuResourceSizeBytes);
+ var renderInterface = new PlatformRenderInterface(options.MaxGpuResourceSizeBytes);
AvaloniaLocator.CurrentMutable
.Bind().ToConstant(renderInterface)
diff --git a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
index 86450690e6..df847d2224 100644
--- a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
+++ b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
@@ -34,7 +34,7 @@ namespace Avalonia.Skia
///
/// Initializes a new instance of the class.
///
- public StreamGeometryImpl() : this(CreateEmptyPath(), Rect.Empty)
+ public StreamGeometryImpl() : this(CreateEmptyPath(), default)
{
}
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index 74e3ebc51d..a7998353d9 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -107,6 +107,8 @@ namespace Avalonia.Skia
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
}
+ public bool IsCorrupted => _gpu?.IsLost == true;
+
///
public Vector Dpi { get; }
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index d437f514bb..dcb267b2a3 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -217,8 +217,7 @@ namespace Avalonia.Skia
public int RowBytes => _bitmap.RowBytes;
///
- public Vector Dpi { get; } = SkiaPlatform.DefaultDpi;
-
+ public Vector Dpi => _parent.Dpi;
///
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
}
diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
index fb7831415d..5fecaef100 100644
--- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
+++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
@@ -17,7 +17,6 @@
-
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 4d307c9762..a5f77230b7 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -113,7 +113,7 @@ namespace Avalonia.Direct2D1
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
}
- public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
+ private IRenderTarget CreateRenderTarget(IEnumerable surfaces)
{
foreach (var s in surfaces)
{
@@ -223,6 +223,26 @@ namespace Avalonia.Direct2D1
return new GlyphRunImpl(run);
}
+ class D2DApi : IPlatformRenderInterfaceContext
+ {
+ private readonly Direct2D1Platform _platform;
+
+ public D2DApi(Direct2D1Platform platform)
+ {
+ _platform = platform;
+ }
+ public object TryGetFeature(Type featureType) => null;
+
+ public void Dispose()
+ {
+ }
+
+ public IRenderTarget CreateRenderTarget(IEnumerable surfaces) => _platform.CreateRenderTarget(surfaces);
+ }
+
+ public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) =>
+ new D2DApi(this);
+
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
diff --git a/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
index 2c0adcac32..02932c52da 100644
--- a/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
+++ b/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs
@@ -38,6 +38,8 @@ namespace Avalonia.Direct2D1
});
}
+ public bool IsCorrupted => false;
+
public IDrawingContextLayerImpl CreateLayer(Size size)
{
var renderTarget = _externalRenderTargetProvider.GetOrCreateRenderTarget();
diff --git a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
index f7e7ed3dc2..984a24fb30 100644
--- a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
+++ b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
@@ -35,6 +35,8 @@ namespace Avalonia.Direct2D1
.CreateDrawingContext(visualBrushRenderer);
}
+ public bool IsCorrupted => false;
+
class FramebufferShim : WicRenderTargetBitmapImpl
{
private readonly ILockedFramebuffer _target;
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
index c5f8e837ce..84f11acdd7 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
@@ -37,6 +37,8 @@ namespace Avalonia.Direct2D1.Media.Imaging
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget, null, () => Version++);
}
+ public bool IsCorrupted => false;
+
public void Blit(IDrawingContextImpl context) => throw new NotSupportedException();
public bool CanBlit => false;
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
index 8c9d01f37d..9f0d48dbc7 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
@@ -39,6 +39,8 @@ namespace Avalonia.Direct2D1.Media
public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
=> CreateDrawingContext(visualBrushRenderer, null);
+ public bool IsCorrupted => false;
+
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer, Action finishedCallback)
{
return new DrawingContextImpl(visualBrushRenderer, null, _renderTarget, finishedCallback: () =>
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
index 885be132a4..2e40bdd9d1 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
@@ -52,7 +52,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
public IntPtr Address => _lock.Data.DataPointer;
public PixelSize Size => _lock.Size.ToAvalonia();
public int RowBytes => _lock.Stride;
- public Vector Dpi { get; } = new Vector(96, 96);
+ public Vector Dpi => _parent.Dpi;
public PixelFormat Format => _format;
}
diff --git a/src/Windows/Avalonia.Direct2D1/OptionalDispose.cs b/src/Windows/Avalonia.Direct2D1/OptionalDispose.cs
index e302e71102..1cdf7661df 100644
--- a/src/Windows/Avalonia.Direct2D1/OptionalDispose.cs
+++ b/src/Windows/Avalonia.Direct2D1/OptionalDispose.cs
@@ -2,7 +2,7 @@
namespace Avalonia.Direct2D1
{
- public readonly struct OptionalDispose : IDisposable where T : IDisposable
+ public readonly record struct OptionalDispose : IDisposable where T : IDisposable
{
private readonly bool _dispose;
diff --git a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs
index d04c616bd9..1a749c1a7f 100644
--- a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs
+++ b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs
@@ -30,6 +30,8 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget);
}
+ public bool IsCorrupted => false;
+
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return D2DRenderTargetBitmapImpl.CreateCompatible(_renderTarget, size);
diff --git a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
index f319cfae03..4935e3db48 100644
--- a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
+++ b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
@@ -35,6 +35,8 @@ namespace Avalonia.Direct2D1
return new DrawingContextImpl(visualBrushRenderer, this, _deviceContext, _swapChain);
}
+ public bool IsCorrupted => false;
+
public IDrawingContextLayerImpl CreateLayer(Size size)
{
if (_deviceContext == null)
diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
index 1dcf45aa4e..16a20c855f 100644
--- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
+++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
@@ -87,9 +87,11 @@ namespace Avalonia.Win32.Interop.Wpf
_ttl.ScalingChanged?.Invoke(_ttl.RenderScaling);
}
+
public IRenderer CreateRenderer(IRenderRoot root)
{
- return new ImmediateRenderer((Visual)root);
+ var mgr = new PlatformRenderInterfaceContextManager(null);
+ return new ImmediateRenderer((Visual)root, () => mgr.CreateRenderTarget(_surfaces), mgr);
}
public void Dispose()
diff --git a/src/Avalonia.OpenGL/AngleOptions.cs b/src/Windows/Avalonia.Win32/AngleOptions.cs
similarity index 90%
rename from src/Avalonia.OpenGL/AngleOptions.cs
rename to src/Windows/Avalonia.Win32/AngleOptions.cs
index 0807eb7ab4..076ddd2a5d 100644
--- a/src/Avalonia.OpenGL/AngleOptions.cs
+++ b/src/Windows/Avalonia.Win32/AngleOptions.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
+using Avalonia.OpenGL;
-namespace Avalonia.OpenGL
+namespace Avalonia.Win32
{
public class AngleOptions
{
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index e69e0ee219..eb51b7fd07 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -16,11 +16,13 @@
+
+
+
+
-
-
$(NoWarn);CA1416
diff --git a/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs b/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs
index 3a67da530f..a749ed1b45 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
internal enum D3D_FEATURE_LEVEL
{
@@ -136,4 +136,53 @@ namespace Avalonia.Win32.DxgiSwapchain
DXGI_ALPHA_MODE_FORCE_DWORD = (unchecked((int)0xffffffff)),
}
+
+ internal enum D3D_DRIVER_TYPE
+ {
+ D3D_DRIVER_TYPE_UNKNOWN = 0,
+ D3D_DRIVER_TYPE_HARDWARE,
+ D3D_DRIVER_TYPE_REFERENCE,
+ D3D_DRIVER_TYPE_NULL,
+ D3D_DRIVER_TYPE_SOFTWARE,
+ D3D_DRIVER_TYPE_WARP
+ }
+
+ internal enum DXGI_ERROR : uint
+ {
+ DXGI_ERROR_ACCESS_DENIED = 0x887A002B,
+ DXGI_ERROR_ACCESS_LOST = 0x887A0026,
+ DXGI_ERROR_ALREADY_EXISTS = 0x887A0036,
+ DXGI_ERROR_CANNOT_PROTECT_CONTENT = 0x887A002A,
+ DXGI_ERROR_DEVICE_HUNG = 0x887A0006,
+ DXGI_ERROR_DEVICE_REMOVED = 0x887A0005,
+ DXGI_ERROR_DEVICE_RESET = 0x887A0007,
+ DXGI_ERROR_DRIVER_INTERNAL_ERROR = 0x887A0020,
+ DXGI_ERROR_FRAME_STATISTICS_DISJOINT = 0x887A000B,
+ DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE = 0x887A000C,
+ DXGI_ERROR_INVALID_CALL = 0x887A0001,
+ DXGI_ERROR_MORE_DATA = 0x887A0003,
+ DXGI_ERROR_NAME_ALREADY_EXISTS = 0x887A002C,
+ DXGI_ERROR_NONEXCLUSIVE = 0x887A0021,
+ DXGI_ERROR_NOT_CURRENTLY_AVAILABLE = 0x887A0022,
+ DXGI_ERROR_NOT_FOUND = 0x887A0002,
+ DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED = 0x887A0023,
+ DXGI_ERROR_REMOTE_OUTOFMEMORY = 0x887A0024,
+ DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE = 0x887A0029,
+ DXGI_ERROR_SDK_COMPONENT_MISSING = 0x887A002D,
+ DXGI_ERROR_SESSION_DISCONNECTED = 0x887A0028,
+ DXGI_ERROR_UNSUPPORTED = 0x887A0004,
+ DXGI_ERROR_WAIT_TIMEOUT = 0x887A0027,
+ DXGI_ERROR_WAS_STILL_DRAWING = 0x887A000A
+ }
+
+ internal static class DxgiErrorExtensions
+ {
+ public static bool IsDeviceLostError(this DXGI_ERROR error)
+ {
+ return error is DXGI_ERROR.DXGI_ERROR_DEVICE_REMOVED
+ or DXGI_ERROR.DXGI_ERROR_DEVICE_HUNG
+ or DXGI_ERROR.DXGI_ERROR_DEVICE_RESET
+ or DXGI_ERROR.DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
+ }
+ }
}
diff --git a/src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs b/src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs
index d606b00109..47451831a6 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs
@@ -6,8 +6,10 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static Avalonia.Win32.Interop.UnmanagedMethods;
+// ReSharper disable InconsistentNaming
+#pragma warning disable CS0649
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
#nullable enable
public unsafe struct HANDLE
@@ -36,1092 +38,13 @@ namespace Avalonia.Win32.DxgiSwapchain
public override string ToString() => ((IntPtr)Value).ToString();
}
- internal unsafe partial struct MONITORINFOEXW
+ internal unsafe struct MONITORINFOEXW
{
internal MONITORINFO Base;
internal fixed ushort szDevice[32];
}
-
- internal unsafe struct DXGI_GAMMA_CONTROL
- {
- public DXGI_RGB Scale;
-
- public DXGI_RGB Offset;
-
- public _GammaCurve_e__FixedBuffer GammaCurve;
-
- public partial struct _GammaCurve_e__FixedBuffer
- {
- public DXGI_RGB e0;
- public DXGI_RGB e1;
- public DXGI_RGB e2;
- public DXGI_RGB e3;
- public DXGI_RGB e4;
- public DXGI_RGB e5;
- public DXGI_RGB e6;
- public DXGI_RGB e7;
- public DXGI_RGB e8;
- public DXGI_RGB e9;
- public DXGI_RGB e10;
- public DXGI_RGB e11;
- public DXGI_RGB e12;
- public DXGI_RGB e13;
- public DXGI_RGB e14;
- public DXGI_RGB e15;
- public DXGI_RGB e16;
- public DXGI_RGB e17;
- public DXGI_RGB e18;
- public DXGI_RGB e19;
- public DXGI_RGB e20;
- public DXGI_RGB e21;
- public DXGI_RGB e22;
- public DXGI_RGB e23;
- public DXGI_RGB e24;
- public DXGI_RGB e25;
- public DXGI_RGB e26;
- public DXGI_RGB e27;
- public DXGI_RGB e28;
- public DXGI_RGB e29;
- public DXGI_RGB e30;
- public DXGI_RGB e31;
- public DXGI_RGB e32;
- public DXGI_RGB e33;
- public DXGI_RGB e34;
- public DXGI_RGB e35;
- public DXGI_RGB e36;
- public DXGI_RGB e37;
- public DXGI_RGB e38;
- public DXGI_RGB e39;
- public DXGI_RGB e40;
- public DXGI_RGB e41;
- public DXGI_RGB e42;
- public DXGI_RGB e43;
- public DXGI_RGB e44;
- public DXGI_RGB e45;
- public DXGI_RGB e46;
- public DXGI_RGB e47;
- public DXGI_RGB e48;
- public DXGI_RGB e49;
- public DXGI_RGB e50;
- public DXGI_RGB e51;
- public DXGI_RGB e52;
- public DXGI_RGB e53;
- public DXGI_RGB e54;
- public DXGI_RGB e55;
- public DXGI_RGB e56;
- public DXGI_RGB e57;
- public DXGI_RGB e58;
- public DXGI_RGB e59;
- public DXGI_RGB e60;
- public DXGI_RGB e61;
- public DXGI_RGB e62;
- public DXGI_RGB e63;
- public DXGI_RGB e64;
- public DXGI_RGB e65;
- public DXGI_RGB e66;
- public DXGI_RGB e67;
- public DXGI_RGB e68;
- public DXGI_RGB e69;
- public DXGI_RGB e70;
- public DXGI_RGB e71;
- public DXGI_RGB e72;
- public DXGI_RGB e73;
- public DXGI_RGB e74;
- public DXGI_RGB e75;
- public DXGI_RGB e76;
- public DXGI_RGB e77;
- public DXGI_RGB e78;
- public DXGI_RGB e79;
- public DXGI_RGB e80;
- public DXGI_RGB e81;
- public DXGI_RGB e82;
- public DXGI_RGB e83;
- public DXGI_RGB e84;
- public DXGI_RGB e85;
- public DXGI_RGB e86;
- public DXGI_RGB e87;
- public DXGI_RGB e88;
- public DXGI_RGB e89;
- public DXGI_RGB e90;
- public DXGI_RGB e91;
- public DXGI_RGB e92;
- public DXGI_RGB e93;
- public DXGI_RGB e94;
- public DXGI_RGB e95;
- public DXGI_RGB e96;
- public DXGI_RGB e97;
- public DXGI_RGB e98;
- public DXGI_RGB e99;
- public DXGI_RGB e100;
- public DXGI_RGB e101;
- public DXGI_RGB e102;
- public DXGI_RGB e103;
- public DXGI_RGB e104;
- public DXGI_RGB e105;
- public DXGI_RGB e106;
- public DXGI_RGB e107;
- public DXGI_RGB e108;
- public DXGI_RGB e109;
- public DXGI_RGB e110;
- public DXGI_RGB e111;
- public DXGI_RGB e112;
- public DXGI_RGB e113;
- public DXGI_RGB e114;
- public DXGI_RGB e115;
- public DXGI_RGB e116;
- public DXGI_RGB e117;
- public DXGI_RGB e118;
- public DXGI_RGB e119;
- public DXGI_RGB e120;
- public DXGI_RGB e121;
- public DXGI_RGB e122;
- public DXGI_RGB e123;
- public DXGI_RGB e124;
- public DXGI_RGB e125;
- public DXGI_RGB e126;
- public DXGI_RGB e127;
- public DXGI_RGB e128;
- public DXGI_RGB e129;
- public DXGI_RGB e130;
- public DXGI_RGB e131;
- public DXGI_RGB e132;
- public DXGI_RGB e133;
- public DXGI_RGB e134;
- public DXGI_RGB e135;
- public DXGI_RGB e136;
- public DXGI_RGB e137;
- public DXGI_RGB e138;
- public DXGI_RGB e139;
- public DXGI_RGB e140;
- public DXGI_RGB e141;
- public DXGI_RGB e142;
- public DXGI_RGB e143;
- public DXGI_RGB e144;
- public DXGI_RGB e145;
- public DXGI_RGB e146;
- public DXGI_RGB e147;
- public DXGI_RGB e148;
- public DXGI_RGB e149;
- public DXGI_RGB e150;
- public DXGI_RGB e151;
- public DXGI_RGB e152;
- public DXGI_RGB e153;
- public DXGI_RGB e154;
- public DXGI_RGB e155;
- public DXGI_RGB e156;
- public DXGI_RGB e157;
- public DXGI_RGB e158;
- public DXGI_RGB e159;
- public DXGI_RGB e160;
- public DXGI_RGB e161;
- public DXGI_RGB e162;
- public DXGI_RGB e163;
- public DXGI_RGB e164;
- public DXGI_RGB e165;
- public DXGI_RGB e166;
- public DXGI_RGB e167;
- public DXGI_RGB e168;
- public DXGI_RGB e169;
- public DXGI_RGB e170;
- public DXGI_RGB e171;
- public DXGI_RGB e172;
- public DXGI_RGB e173;
- public DXGI_RGB e174;
- public DXGI_RGB e175;
- public DXGI_RGB e176;
- public DXGI_RGB e177;
- public DXGI_RGB e178;
- public DXGI_RGB e179;
- public DXGI_RGB e180;
- public DXGI_RGB e181;
- public DXGI_RGB e182;
- public DXGI_RGB e183;
- public DXGI_RGB e184;
- public DXGI_RGB e185;
- public DXGI_RGB e186;
- public DXGI_RGB e187;
- public DXGI_RGB e188;
- public DXGI_RGB e189;
- public DXGI_RGB e190;
- public DXGI_RGB e191;
- public DXGI_RGB e192;
- public DXGI_RGB e193;
- public DXGI_RGB e194;
- public DXGI_RGB e195;
- public DXGI_RGB e196;
- public DXGI_RGB e197;
- public DXGI_RGB e198;
- public DXGI_RGB e199;
- public DXGI_RGB e200;
- public DXGI_RGB e201;
- public DXGI_RGB e202;
- public DXGI_RGB e203;
- public DXGI_RGB e204;
- public DXGI_RGB e205;
- public DXGI_RGB e206;
- public DXGI_RGB e207;
- public DXGI_RGB e208;
- public DXGI_RGB e209;
- public DXGI_RGB e210;
- public DXGI_RGB e211;
- public DXGI_RGB e212;
- public DXGI_RGB e213;
- public DXGI_RGB e214;
- public DXGI_RGB e215;
- public DXGI_RGB e216;
- public DXGI_RGB e217;
- public DXGI_RGB e218;
- public DXGI_RGB e219;
- public DXGI_RGB e220;
- public DXGI_RGB e221;
- public DXGI_RGB e222;
- public DXGI_RGB e223;
- public DXGI_RGB e224;
- public DXGI_RGB e225;
- public DXGI_RGB e226;
- public DXGI_RGB e227;
- public DXGI_RGB e228;
- public DXGI_RGB e229;
- public DXGI_RGB e230;
- public DXGI_RGB e231;
- public DXGI_RGB e232;
- public DXGI_RGB e233;
- public DXGI_RGB e234;
- public DXGI_RGB e235;
- public DXGI_RGB e236;
- public DXGI_RGB e237;
- public DXGI_RGB e238;
- public DXGI_RGB e239;
- public DXGI_RGB e240;
- public DXGI_RGB e241;
- public DXGI_RGB e242;
- public DXGI_RGB e243;
- public DXGI_RGB e244;
- public DXGI_RGB e245;
- public DXGI_RGB e246;
- public DXGI_RGB e247;
- public DXGI_RGB e248;
- public DXGI_RGB e249;
- public DXGI_RGB e250;
- public DXGI_RGB e251;
- public DXGI_RGB e252;
- public DXGI_RGB e253;
- public DXGI_RGB e254;
- public DXGI_RGB e255;
- public DXGI_RGB e256;
- public DXGI_RGB e257;
- public DXGI_RGB e258;
- public DXGI_RGB e259;
- public DXGI_RGB e260;
- public DXGI_RGB e261;
- public DXGI_RGB e262;
- public DXGI_RGB e263;
- public DXGI_RGB e264;
- public DXGI_RGB e265;
- public DXGI_RGB e266;
- public DXGI_RGB e267;
- public DXGI_RGB e268;
- public DXGI_RGB e269;
- public DXGI_RGB e270;
- public DXGI_RGB e271;
- public DXGI_RGB e272;
- public DXGI_RGB e273;
- public DXGI_RGB e274;
- public DXGI_RGB e275;
- public DXGI_RGB e276;
- public DXGI_RGB e277;
- public DXGI_RGB e278;
- public DXGI_RGB e279;
- public DXGI_RGB e280;
- public DXGI_RGB e281;
- public DXGI_RGB e282;
- public DXGI_RGB e283;
- public DXGI_RGB e284;
- public DXGI_RGB e285;
- public DXGI_RGB e286;
- public DXGI_RGB e287;
- public DXGI_RGB e288;
- public DXGI_RGB e289;
- public DXGI_RGB e290;
- public DXGI_RGB e291;
- public DXGI_RGB e292;
- public DXGI_RGB e293;
- public DXGI_RGB e294;
- public DXGI_RGB e295;
- public DXGI_RGB e296;
- public DXGI_RGB e297;
- public DXGI_RGB e298;
- public DXGI_RGB e299;
- public DXGI_RGB e300;
- public DXGI_RGB e301;
- public DXGI_RGB e302;
- public DXGI_RGB e303;
- public DXGI_RGB e304;
- public DXGI_RGB e305;
- public DXGI_RGB e306;
- public DXGI_RGB e307;
- public DXGI_RGB e308;
- public DXGI_RGB e309;
- public DXGI_RGB e310;
- public DXGI_RGB e311;
- public DXGI_RGB e312;
- public DXGI_RGB e313;
- public DXGI_RGB e314;
- public DXGI_RGB e315;
- public DXGI_RGB e316;
- public DXGI_RGB e317;
- public DXGI_RGB e318;
- public DXGI_RGB e319;
- public DXGI_RGB e320;
- public DXGI_RGB e321;
- public DXGI_RGB e322;
- public DXGI_RGB e323;
- public DXGI_RGB e324;
- public DXGI_RGB e325;
- public DXGI_RGB e326;
- public DXGI_RGB e327;
- public DXGI_RGB e328;
- public DXGI_RGB e329;
- public DXGI_RGB e330;
- public DXGI_RGB e331;
- public DXGI_RGB e332;
- public DXGI_RGB e333;
- public DXGI_RGB e334;
- public DXGI_RGB e335;
- public DXGI_RGB e336;
- public DXGI_RGB e337;
- public DXGI_RGB e338;
- public DXGI_RGB e339;
- public DXGI_RGB e340;
- public DXGI_RGB e341;
- public DXGI_RGB e342;
- public DXGI_RGB e343;
- public DXGI_RGB e344;
- public DXGI_RGB e345;
- public DXGI_RGB e346;
- public DXGI_RGB e347;
- public DXGI_RGB e348;
- public DXGI_RGB e349;
- public DXGI_RGB e350;
- public DXGI_RGB e351;
- public DXGI_RGB e352;
- public DXGI_RGB e353;
- public DXGI_RGB e354;
- public DXGI_RGB e355;
- public DXGI_RGB e356;
- public DXGI_RGB e357;
- public DXGI_RGB e358;
- public DXGI_RGB e359;
- public DXGI_RGB e360;
- public DXGI_RGB e361;
- public DXGI_RGB e362;
- public DXGI_RGB e363;
- public DXGI_RGB e364;
- public DXGI_RGB e365;
- public DXGI_RGB e366;
- public DXGI_RGB e367;
- public DXGI_RGB e368;
- public DXGI_RGB e369;
- public DXGI_RGB e370;
- public DXGI_RGB e371;
- public DXGI_RGB e372;
- public DXGI_RGB e373;
- public DXGI_RGB e374;
- public DXGI_RGB e375;
- public DXGI_RGB e376;
- public DXGI_RGB e377;
- public DXGI_RGB e378;
- public DXGI_RGB e379;
- public DXGI_RGB e380;
- public DXGI_RGB e381;
- public DXGI_RGB e382;
- public DXGI_RGB e383;
- public DXGI_RGB e384;
- public DXGI_RGB e385;
- public DXGI_RGB e386;
- public DXGI_RGB e387;
- public DXGI_RGB e388;
- public DXGI_RGB e389;
- public DXGI_RGB e390;
- public DXGI_RGB e391;
- public DXGI_RGB e392;
- public DXGI_RGB e393;
- public DXGI_RGB e394;
- public DXGI_RGB e395;
- public DXGI_RGB e396;
- public DXGI_RGB e397;
- public DXGI_RGB e398;
- public DXGI_RGB e399;
- public DXGI_RGB e400;
- public DXGI_RGB e401;
- public DXGI_RGB e402;
- public DXGI_RGB e403;
- public DXGI_RGB e404;
- public DXGI_RGB e405;
- public DXGI_RGB e406;
- public DXGI_RGB e407;
- public DXGI_RGB e408;
- public DXGI_RGB e409;
- public DXGI_RGB e410;
- public DXGI_RGB e411;
- public DXGI_RGB e412;
- public DXGI_RGB e413;
- public DXGI_RGB e414;
- public DXGI_RGB e415;
- public DXGI_RGB e416;
- public DXGI_RGB e417;
- public DXGI_RGB e418;
- public DXGI_RGB e419;
- public DXGI_RGB e420;
- public DXGI_RGB e421;
- public DXGI_RGB e422;
- public DXGI_RGB e423;
- public DXGI_RGB e424;
- public DXGI_RGB e425;
- public DXGI_RGB e426;
- public DXGI_RGB e427;
- public DXGI_RGB e428;
- public DXGI_RGB e429;
- public DXGI_RGB e430;
- public DXGI_RGB e431;
- public DXGI_RGB e432;
- public DXGI_RGB e433;
- public DXGI_RGB e434;
- public DXGI_RGB e435;
- public DXGI_RGB e436;
- public DXGI_RGB e437;
- public DXGI_RGB e438;
- public DXGI_RGB e439;
- public DXGI_RGB e440;
- public DXGI_RGB e441;
- public DXGI_RGB e442;
- public DXGI_RGB e443;
- public DXGI_RGB e444;
- public DXGI_RGB e445;
- public DXGI_RGB e446;
- public DXGI_RGB e447;
- public DXGI_RGB e448;
- public DXGI_RGB e449;
- public DXGI_RGB e450;
- public DXGI_RGB e451;
- public DXGI_RGB e452;
- public DXGI_RGB e453;
- public DXGI_RGB e454;
- public DXGI_RGB e455;
- public DXGI_RGB e456;
- public DXGI_RGB e457;
- public DXGI_RGB e458;
- public DXGI_RGB e459;
- public DXGI_RGB e460;
- public DXGI_RGB e461;
- public DXGI_RGB e462;
- public DXGI_RGB e463;
- public DXGI_RGB e464;
- public DXGI_RGB e465;
- public DXGI_RGB e466;
- public DXGI_RGB e467;
- public DXGI_RGB e468;
- public DXGI_RGB e469;
- public DXGI_RGB e470;
- public DXGI_RGB e471;
- public DXGI_RGB e472;
- public DXGI_RGB e473;
- public DXGI_RGB e474;
- public DXGI_RGB e475;
- public DXGI_RGB e476;
- public DXGI_RGB e477;
- public DXGI_RGB e478;
- public DXGI_RGB e479;
- public DXGI_RGB e480;
- public DXGI_RGB e481;
- public DXGI_RGB e482;
- public DXGI_RGB e483;
- public DXGI_RGB e484;
- public DXGI_RGB e485;
- public DXGI_RGB e486;
- public DXGI_RGB e487;
- public DXGI_RGB e488;
- public DXGI_RGB e489;
- public DXGI_RGB e490;
- public DXGI_RGB e491;
- public DXGI_RGB e492;
- public DXGI_RGB e493;
- public DXGI_RGB e494;
- public DXGI_RGB e495;
- public DXGI_RGB e496;
- public DXGI_RGB e497;
- public DXGI_RGB e498;
- public DXGI_RGB e499;
- public DXGI_RGB e500;
- public DXGI_RGB e501;
- public DXGI_RGB e502;
- public DXGI_RGB e503;
- public DXGI_RGB e504;
- public DXGI_RGB e505;
- public DXGI_RGB e506;
- public DXGI_RGB e507;
- public DXGI_RGB e508;
- public DXGI_RGB e509;
- public DXGI_RGB e510;
- public DXGI_RGB e511;
- public DXGI_RGB e512;
- public DXGI_RGB e513;
- public DXGI_RGB e514;
- public DXGI_RGB e515;
- public DXGI_RGB e516;
- public DXGI_RGB e517;
- public DXGI_RGB e518;
- public DXGI_RGB e519;
- public DXGI_RGB e520;
- public DXGI_RGB e521;
- public DXGI_RGB e522;
- public DXGI_RGB e523;
- public DXGI_RGB e524;
- public DXGI_RGB e525;
- public DXGI_RGB e526;
- public DXGI_RGB e527;
- public DXGI_RGB e528;
- public DXGI_RGB e529;
- public DXGI_RGB e530;
- public DXGI_RGB e531;
- public DXGI_RGB e532;
- public DXGI_RGB e533;
- public DXGI_RGB e534;
- public DXGI_RGB e535;
- public DXGI_RGB e536;
- public DXGI_RGB e537;
- public DXGI_RGB e538;
- public DXGI_RGB e539;
- public DXGI_RGB e540;
- public DXGI_RGB e541;
- public DXGI_RGB e542;
- public DXGI_RGB e543;
- public DXGI_RGB e544;
- public DXGI_RGB e545;
- public DXGI_RGB e546;
- public DXGI_RGB e547;
- public DXGI_RGB e548;
- public DXGI_RGB e549;
- public DXGI_RGB e550;
- public DXGI_RGB e551;
- public DXGI_RGB e552;
- public DXGI_RGB e553;
- public DXGI_RGB e554;
- public DXGI_RGB e555;
- public DXGI_RGB e556;
- public DXGI_RGB e557;
- public DXGI_RGB e558;
- public DXGI_RGB e559;
- public DXGI_RGB e560;
- public DXGI_RGB e561;
- public DXGI_RGB e562;
- public DXGI_RGB e563;
- public DXGI_RGB e564;
- public DXGI_RGB e565;
- public DXGI_RGB e566;
- public DXGI_RGB e567;
- public DXGI_RGB e568;
- public DXGI_RGB e569;
- public DXGI_RGB e570;
- public DXGI_RGB e571;
- public DXGI_RGB e572;
- public DXGI_RGB e573;
- public DXGI_RGB e574;
- public DXGI_RGB e575;
- public DXGI_RGB e576;
- public DXGI_RGB e577;
- public DXGI_RGB e578;
- public DXGI_RGB e579;
- public DXGI_RGB e580;
- public DXGI_RGB e581;
- public DXGI_RGB e582;
- public DXGI_RGB e583;
- public DXGI_RGB e584;
- public DXGI_RGB e585;
- public DXGI_RGB e586;
- public DXGI_RGB e587;
- public DXGI_RGB e588;
- public DXGI_RGB e589;
- public DXGI_RGB e590;
- public DXGI_RGB e591;
- public DXGI_RGB e592;
- public DXGI_RGB e593;
- public DXGI_RGB e594;
- public DXGI_RGB e595;
- public DXGI_RGB e596;
- public DXGI_RGB e597;
- public DXGI_RGB e598;
- public DXGI_RGB e599;
- public DXGI_RGB e600;
- public DXGI_RGB e601;
- public DXGI_RGB e602;
- public DXGI_RGB e603;
- public DXGI_RGB e604;
- public DXGI_RGB e605;
- public DXGI_RGB e606;
- public DXGI_RGB e607;
- public DXGI_RGB e608;
- public DXGI_RGB e609;
- public DXGI_RGB e610;
- public DXGI_RGB e611;
- public DXGI_RGB e612;
- public DXGI_RGB e613;
- public DXGI_RGB e614;
- public DXGI_RGB e615;
- public DXGI_RGB e616;
- public DXGI_RGB e617;
- public DXGI_RGB e618;
- public DXGI_RGB e619;
- public DXGI_RGB e620;
- public DXGI_RGB e621;
- public DXGI_RGB e622;
- public DXGI_RGB e623;
- public DXGI_RGB e624;
- public DXGI_RGB e625;
- public DXGI_RGB e626;
- public DXGI_RGB e627;
- public DXGI_RGB e628;
- public DXGI_RGB e629;
- public DXGI_RGB e630;
- public DXGI_RGB e631;
- public DXGI_RGB e632;
- public DXGI_RGB e633;
- public DXGI_RGB e634;
- public DXGI_RGB e635;
- public DXGI_RGB e636;
- public DXGI_RGB e637;
- public DXGI_RGB e638;
- public DXGI_RGB e639;
- public DXGI_RGB e640;
- public DXGI_RGB e641;
- public DXGI_RGB e642;
- public DXGI_RGB e643;
- public DXGI_RGB e644;
- public DXGI_RGB e645;
- public DXGI_RGB e646;
- public DXGI_RGB e647;
- public DXGI_RGB e648;
- public DXGI_RGB e649;
- public DXGI_RGB e650;
- public DXGI_RGB e651;
- public DXGI_RGB e652;
- public DXGI_RGB e653;
- public DXGI_RGB e654;
- public DXGI_RGB e655;
- public DXGI_RGB e656;
- public DXGI_RGB e657;
- public DXGI_RGB e658;
- public DXGI_RGB e659;
- public DXGI_RGB e660;
- public DXGI_RGB e661;
- public DXGI_RGB e662;
- public DXGI_RGB e663;
- public DXGI_RGB e664;
- public DXGI_RGB e665;
- public DXGI_RGB e666;
- public DXGI_RGB e667;
- public DXGI_RGB e668;
- public DXGI_RGB e669;
- public DXGI_RGB e670;
- public DXGI_RGB e671;
- public DXGI_RGB e672;
- public DXGI_RGB e673;
- public DXGI_RGB e674;
- public DXGI_RGB e675;
- public DXGI_RGB e676;
- public DXGI_RGB e677;
- public DXGI_RGB e678;
- public DXGI_RGB e679;
- public DXGI_RGB e680;
- public DXGI_RGB e681;
- public DXGI_RGB e682;
- public DXGI_RGB e683;
- public DXGI_RGB e684;
- public DXGI_RGB e685;
- public DXGI_RGB e686;
- public DXGI_RGB e687;
- public DXGI_RGB e688;
- public DXGI_RGB e689;
- public DXGI_RGB e690;
- public DXGI_RGB e691;
- public DXGI_RGB e692;
- public DXGI_RGB e693;
- public DXGI_RGB e694;
- public DXGI_RGB e695;
- public DXGI_RGB e696;
- public DXGI_RGB e697;
- public DXGI_RGB e698;
- public DXGI_RGB e699;
- public DXGI_RGB e700;
- public DXGI_RGB e701;
- public DXGI_RGB e702;
- public DXGI_RGB e703;
- public DXGI_RGB e704;
- public DXGI_RGB e705;
- public DXGI_RGB e706;
- public DXGI_RGB e707;
- public DXGI_RGB e708;
- public DXGI_RGB e709;
- public DXGI_RGB e710;
- public DXGI_RGB e711;
- public DXGI_RGB e712;
- public DXGI_RGB e713;
- public DXGI_RGB e714;
- public DXGI_RGB e715;
- public DXGI_RGB e716;
- public DXGI_RGB e717;
- public DXGI_RGB e718;
- public DXGI_RGB e719;
- public DXGI_RGB e720;
- public DXGI_RGB e721;
- public DXGI_RGB e722;
- public DXGI_RGB e723;
- public DXGI_RGB e724;
- public DXGI_RGB e725;
- public DXGI_RGB e726;
- public DXGI_RGB e727;
- public DXGI_RGB e728;
- public DXGI_RGB e729;
- public DXGI_RGB e730;
- public DXGI_RGB e731;
- public DXGI_RGB e732;
- public DXGI_RGB e733;
- public DXGI_RGB e734;
- public DXGI_RGB e735;
- public DXGI_RGB e736;
- public DXGI_RGB e737;
- public DXGI_RGB e738;
- public DXGI_RGB e739;
- public DXGI_RGB e740;
- public DXGI_RGB e741;
- public DXGI_RGB e742;
- public DXGI_RGB e743;
- public DXGI_RGB e744;
- public DXGI_RGB e745;
- public DXGI_RGB e746;
- public DXGI_RGB e747;
- public DXGI_RGB e748;
- public DXGI_RGB e749;
- public DXGI_RGB e750;
- public DXGI_RGB e751;
- public DXGI_RGB e752;
- public DXGI_RGB e753;
- public DXGI_RGB e754;
- public DXGI_RGB e755;
- public DXGI_RGB e756;
- public DXGI_RGB e757;
- public DXGI_RGB e758;
- public DXGI_RGB e759;
- public DXGI_RGB e760;
- public DXGI_RGB e761;
- public DXGI_RGB e762;
- public DXGI_RGB e763;
- public DXGI_RGB e764;
- public DXGI_RGB e765;
- public DXGI_RGB e766;
- public DXGI_RGB e767;
- public DXGI_RGB e768;
- public DXGI_RGB e769;
- public DXGI_RGB e770;
- public DXGI_RGB e771;
- public DXGI_RGB e772;
- public DXGI_RGB e773;
- public DXGI_RGB e774;
- public DXGI_RGB e775;
- public DXGI_RGB e776;
- public DXGI_RGB e777;
- public DXGI_RGB e778;
- public DXGI_RGB e779;
- public DXGI_RGB e780;
- public DXGI_RGB e781;
- public DXGI_RGB e782;
- public DXGI_RGB e783;
- public DXGI_RGB e784;
- public DXGI_RGB e785;
- public DXGI_RGB e786;
- public DXGI_RGB e787;
- public DXGI_RGB e788;
- public DXGI_RGB e789;
- public DXGI_RGB e790;
- public DXGI_RGB e791;
- public DXGI_RGB e792;
- public DXGI_RGB e793;
- public DXGI_RGB e794;
- public DXGI_RGB e795;
- public DXGI_RGB e796;
- public DXGI_RGB e797;
- public DXGI_RGB e798;
- public DXGI_RGB e799;
- public DXGI_RGB e800;
- public DXGI_RGB e801;
- public DXGI_RGB e802;
- public DXGI_RGB e803;
- public DXGI_RGB e804;
- public DXGI_RGB e805;
- public DXGI_RGB e806;
- public DXGI_RGB e807;
- public DXGI_RGB e808;
- public DXGI_RGB e809;
- public DXGI_RGB e810;
- public DXGI_RGB e811;
- public DXGI_RGB e812;
- public DXGI_RGB e813;
- public DXGI_RGB e814;
- public DXGI_RGB e815;
- public DXGI_RGB e816;
- public DXGI_RGB e817;
- public DXGI_RGB e818;
- public DXGI_RGB e819;
- public DXGI_RGB e820;
- public DXGI_RGB e821;
- public DXGI_RGB e822;
- public DXGI_RGB e823;
- public DXGI_RGB e824;
- public DXGI_RGB e825;
- public DXGI_RGB e826;
- public DXGI_RGB e827;
- public DXGI_RGB e828;
- public DXGI_RGB e829;
- public DXGI_RGB e830;
- public DXGI_RGB e831;
- public DXGI_RGB e832;
- public DXGI_RGB e833;
- public DXGI_RGB e834;
- public DXGI_RGB e835;
- public DXGI_RGB e836;
- public DXGI_RGB e837;
- public DXGI_RGB e838;
- public DXGI_RGB e839;
- public DXGI_RGB e840;
- public DXGI_RGB e841;
- public DXGI_RGB e842;
- public DXGI_RGB e843;
- public DXGI_RGB e844;
- public DXGI_RGB e845;
- public DXGI_RGB e846;
- public DXGI_RGB e847;
- public DXGI_RGB e848;
- public DXGI_RGB e849;
- public DXGI_RGB e850;
- public DXGI_RGB e851;
- public DXGI_RGB e852;
- public DXGI_RGB e853;
- public DXGI_RGB e854;
- public DXGI_RGB e855;
- public DXGI_RGB e856;
- public DXGI_RGB e857;
- public DXGI_RGB e858;
- public DXGI_RGB e859;
- public DXGI_RGB e860;
- public DXGI_RGB e861;
- public DXGI_RGB e862;
- public DXGI_RGB e863;
- public DXGI_RGB e864;
- public DXGI_RGB e865;
- public DXGI_RGB e866;
- public DXGI_RGB e867;
- public DXGI_RGB e868;
- public DXGI_RGB e869;
- public DXGI_RGB e870;
- public DXGI_RGB e871;
- public DXGI_RGB e872;
- public DXGI_RGB e873;
- public DXGI_RGB e874;
- public DXGI_RGB e875;
- public DXGI_RGB e876;
- public DXGI_RGB e877;
- public DXGI_RGB e878;
- public DXGI_RGB e879;
- public DXGI_RGB e880;
- public DXGI_RGB e881;
- public DXGI_RGB e882;
- public DXGI_RGB e883;
- public DXGI_RGB e884;
- public DXGI_RGB e885;
- public DXGI_RGB e886;
- public DXGI_RGB e887;
- public DXGI_RGB e888;
- public DXGI_RGB e889;
- public DXGI_RGB e890;
- public DXGI_RGB e891;
- public DXGI_RGB e892;
- public DXGI_RGB e893;
- public DXGI_RGB e894;
- public DXGI_RGB e895;
- public DXGI_RGB e896;
- public DXGI_RGB e897;
- public DXGI_RGB e898;
- public DXGI_RGB e899;
- public DXGI_RGB e900;
- public DXGI_RGB e901;
- public DXGI_RGB e902;
- public DXGI_RGB e903;
- public DXGI_RGB e904;
- public DXGI_RGB e905;
- public DXGI_RGB e906;
- public DXGI_RGB e907;
- public DXGI_RGB e908;
- public DXGI_RGB e909;
- public DXGI_RGB e910;
- public DXGI_RGB e911;
- public DXGI_RGB e912;
- public DXGI_RGB e913;
- public DXGI_RGB e914;
- public DXGI_RGB e915;
- public DXGI_RGB e916;
- public DXGI_RGB e917;
- public DXGI_RGB e918;
- public DXGI_RGB e919;
- public DXGI_RGB e920;
- public DXGI_RGB e921;
- public DXGI_RGB e922;
- public DXGI_RGB e923;
- public DXGI_RGB e924;
- public DXGI_RGB e925;
- public DXGI_RGB e926;
- public DXGI_RGB e927;
- public DXGI_RGB e928;
- public DXGI_RGB e929;
- public DXGI_RGB e930;
- public DXGI_RGB e931;
- public DXGI_RGB e932;
- public DXGI_RGB e933;
- public DXGI_RGB e934;
- public DXGI_RGB e935;
- public DXGI_RGB e936;
- public DXGI_RGB e937;
- public DXGI_RGB e938;
- public DXGI_RGB e939;
- public DXGI_RGB e940;
- public DXGI_RGB e941;
- public DXGI_RGB e942;
- public DXGI_RGB e943;
- public DXGI_RGB e944;
- public DXGI_RGB e945;
- public DXGI_RGB e946;
- public DXGI_RGB e947;
- public DXGI_RGB e948;
- public DXGI_RGB e949;
- public DXGI_RGB e950;
- public DXGI_RGB e951;
- public DXGI_RGB e952;
- public DXGI_RGB e953;
- public DXGI_RGB e954;
- public DXGI_RGB e955;
- public DXGI_RGB e956;
- public DXGI_RGB e957;
- public DXGI_RGB e958;
- public DXGI_RGB e959;
- public DXGI_RGB e960;
- public DXGI_RGB e961;
- public DXGI_RGB e962;
- public DXGI_RGB e963;
- public DXGI_RGB e964;
- public DXGI_RGB e965;
- public DXGI_RGB e966;
- public DXGI_RGB e967;
- public DXGI_RGB e968;
- public DXGI_RGB e969;
- public DXGI_RGB e970;
- public DXGI_RGB e971;
- public DXGI_RGB e972;
- public DXGI_RGB e973;
- public DXGI_RGB e974;
- public DXGI_RGB e975;
- public DXGI_RGB e976;
- public DXGI_RGB e977;
- public DXGI_RGB e978;
- public DXGI_RGB e979;
- public DXGI_RGB e980;
- public DXGI_RGB e981;
- public DXGI_RGB e982;
- public DXGI_RGB e983;
- public DXGI_RGB e984;
- public DXGI_RGB e985;
- public DXGI_RGB e986;
- public DXGI_RGB e987;
- public DXGI_RGB e988;
- public DXGI_RGB e989;
- public DXGI_RGB e990;
- public DXGI_RGB e991;
- public DXGI_RGB e992;
- public DXGI_RGB e993;
- public DXGI_RGB e994;
- public DXGI_RGB e995;
- public DXGI_RGB e996;
- public DXGI_RGB e997;
- public DXGI_RGB e998;
- public DXGI_RGB e999;
- public DXGI_RGB e1000;
- public DXGI_RGB e1001;
- public DXGI_RGB e1002;
- public DXGI_RGB e1003;
- public DXGI_RGB e1004;
- public DXGI_RGB e1005;
- public DXGI_RGB e1006;
- public DXGI_RGB e1007;
- public DXGI_RGB e1008;
- public DXGI_RGB e1009;
- public DXGI_RGB e1010;
- public DXGI_RGB e1011;
- public DXGI_RGB e1012;
- public DXGI_RGB e1013;
- public DXGI_RGB e1014;
- public DXGI_RGB e1015;
- public DXGI_RGB e1016;
- public DXGI_RGB e1017;
- public DXGI_RGB e1018;
- public DXGI_RGB e1019;
- public DXGI_RGB e1020;
- public DXGI_RGB e1021;
- public DXGI_RGB e1022;
- public DXGI_RGB e1023;
- public DXGI_RGB e1024;
-#if NET6_0_OR_GREATER
- public ref DXGI_RGB this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return ref AsSpan()[index];
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span AsSpan() => MemoryMarshal.CreateSpan(ref e0, 1025);
-#else
- // there is no way to do this outside of terrible unsafe code. Don't do this in .Net Standard 2.0
-
- public DXGI_RGB this[int index]
- {
- get
- {
- if ((uint)index > 1025)
- throw new ArgumentOutOfRangeException("index");
-
- fixed (DXGI_RGB* basePtr = &e0)
- {
- DXGI_RGB* newPtr = basePtr + index;
- return *newPtr;
- }
- }
- set
- {
- if ((uint)index > 1025)
- throw new ArgumentOutOfRangeException("index");
-
- fixed (DXGI_RGB* basePtr = &e0)
- {
- DXGI_RGB* newPtr = basePtr + index;
- *newPtr = value;
- }
- }
- }
-#endif
- }
- }
-
+
internal unsafe struct DEVMODEW
{
public fixed ushort dmDeviceName[32];
@@ -1196,7 +119,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public uint Flags;
}
- internal unsafe struct DXGI_FRAME_STATISTICS
+ internal struct DXGI_FRAME_STATISTICS
{
public uint PresentCount;
@@ -1225,11 +148,10 @@ namespace Avalonia.Win32.DxgiSwapchain
internal unsafe struct DXGI_MAPPED_RECT
{
public int Pitch;
-
public byte* pBits;
}
- internal unsafe partial struct DXGI_MODE_DESC
+ internal struct DXGI_MODE_DESC
{
public ushort Width;
public ushort Height;
@@ -1239,7 +161,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public DXGI_MODE_SCALING Scaling;
}
- internal unsafe partial struct DXGI_OUTPUT_DESC
+ internal unsafe struct DXGI_OUTPUT_DESC
{
internal fixed ushort DeviceName[32];
@@ -1263,13 +185,13 @@ namespace Avalonia.Win32.DxgiSwapchain
public POINT* pScrollOffset;
}
- internal unsafe partial struct DXGI_RATIONAL
+ internal struct DXGI_RATIONAL
{
public ushort Numerator;
public ushort Denominator;
}
- internal partial struct DXGI_RGB
+ internal struct DXGI_RGB
{
public float Red;
@@ -1278,7 +200,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public float Blue;
}
- internal partial struct DXGI_RGBA
+ internal struct DXGI_RGBA
{
public float r;
@@ -1295,7 +217,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public uint Quality;
}
- internal unsafe struct DXGI_SURFACE_DESC
+ internal struct DXGI_SURFACE_DESC
{
public uint Width;
@@ -1306,7 +228,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public DXGI_SAMPLE_DESC SampleDesc;
}
- internal unsafe partial struct DXGI_SWAP_CHAIN_DESC
+ internal struct DXGI_SWAP_CHAIN_DESC
{
public DXGI_MODE_DESC BufferDesc;
public DXGI_SAMPLE_DESC SampleDesc;
@@ -1333,7 +255,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public uint Flags;
}
- internal unsafe struct DXGI_SWAP_CHAIN_FULLSCREEN_DESC
+ internal struct DXGI_SWAP_CHAIN_FULLSCREEN_DESC
{
public DXGI_RATIONAL RefreshRate;
@@ -1344,7 +266,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public int Windowed;
}
- internal partial struct D3D11_TEXTURE2D_DESC
+ internal struct D3D11_TEXTURE2D_DESC
{
public uint Width;
diff --git a/src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs b/src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs
index 1a739ce3d1..14a9ff1277 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs
@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
internal unsafe class DirectXUnmanagedMethods
{
@@ -23,7 +23,16 @@ namespace Avalonia.Win32.DxgiSwapchain
[DllImport("user32", ExactSpelling = true)]
internal static extern bool EnumDisplaySettingsW(ushort* lpszDeviceName, uint iModeNum, DEVMODEW* lpDevMode);
- [DllImport("user32", ExactSpelling = true, SetLastError = true)]
- internal static extern bool GetClientRect(IntPtr hWnd, Interop.UnmanagedMethods.RECT* lpRect);
+ [DllImport("d3d11", ExactSpelling = true, PreserveSig = false)]
+ public static extern void D3D11CreateDevice(
+ IntPtr adapter, D3D_DRIVER_TYPE DriverType,
+ IntPtr Software,
+ uint Flags,
+ D3D_FEATURE_LEVEL[] pFeatureLevels,
+ uint FeatureLevels,
+ uint SDKVersion,
+ out IntPtr ppDevice,
+ out D3D_FEATURE_LEVEL pFeatureLevel,
+ IntPtr* ppImmediateContext);
}
}
diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
index 8c13ecdcc1..e82f7633be 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
@@ -10,11 +10,12 @@ using Avalonia.Logging;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
+using Avalonia.Win32.OpenGl.Angle;
using static Avalonia.Win32.Interop.UnmanagedMethods;
-using static Avalonia.Win32.DxgiSwapchain.DirectXUnmanagedMethods;
+using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods;
using MicroCom.Runtime;
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
#pragma warning disable CA1416 // This should only be reachable on Windows
#nullable enable
@@ -25,34 +26,27 @@ namespace Avalonia.Win32.DxgiSwapchain
public bool RunsInBackground => true;
public event Action? Tick;
-
- private AngleWin32EglDisplay _angle;
- private EglPlatformOpenGlInterface _gl;
private object _syncLock;
private IDXGIOutput? _output = null;
private Stopwatch? _stopwatch = null;
+ private const string LogArea = "DXGI";
- public DxgiConnection(EglPlatformOpenGlInterface gl, object syncLock)
+ public DxgiConnection(object syncLock)
{
-
_syncLock = syncLock;
- _angle = (AngleWin32EglDisplay)gl.Display;
- _gl = gl;
}
-
- public EglPlatformOpenGlInterface Egl => _gl;
-
- public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle)
+
+ public static void TryCreateAndRegister()
{
try
{
- TryCreateAndRegisterCore(angle);
+ TryCreateAndRegisterCore();
}
catch (Exception ex)
{
- Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
+ Logger.TryGet(LogEventLevel.Error, LogArea)
?.Log(null, "Unable to establish Dxgi: {0}", ex);
}
}
@@ -66,7 +60,7 @@ namespace Avalonia.Win32.DxgiSwapchain
}
catch (Exception ex)
{
- Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
+ Logger.TryGet(LogEventLevel.Error, LogArea)
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
}
@@ -84,7 +78,7 @@ namespace Avalonia.Win32.DxgiSwapchain
}
catch (Exception ex)
{
- Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
+ Logger.TryGet(LogEventLevel.Error, LogArea)
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
_output.Dispose();
_output = null;
@@ -103,7 +97,7 @@ namespace Avalonia.Win32.DxgiSwapchain
}
catch (Exception ex)
{
- Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
+ Logger.TryGet(LogEventLevel.Error, LogArea)
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
}
}
@@ -168,7 +162,7 @@ namespace Avalonia.Win32.DxgiSwapchain
}
// Used the windows composition as a blueprint for this startup/creation
- static private bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface gl)
+ static private bool TryCreateAndRegisterCore()
{
var tcs = new TaskCompletionSource();
var pumpLock = new object();
@@ -178,7 +172,7 @@ namespace Avalonia.Win32.DxgiSwapchain
{
DxgiConnection connection;
- connection = new DxgiConnection(gl, pumpLock);
+ connection = new DxgiConnection(pumpLock);
AvaloniaLocator.CurrentMutable.BindToSelf(connection);
AvaloniaLocator.CurrentMutable.Bind().ToConstant(connection);
diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs
index 2ab9b30d43..cb7826e185 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs
@@ -8,11 +8,12 @@ using System.Threading.Tasks;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
+using Avalonia.Win32.OpenGl.Angle;
using MicroCom.Runtime;
using static Avalonia.OpenGL.Egl.EglGlPlatformSurfaceBase;
using static Avalonia.Win32.Interop.UnmanagedMethods;
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
#pragma warning disable CA1416 // Validate platform compatibility, if you enter this not on windows you have messed up badly
#nullable enable
@@ -22,8 +23,7 @@ namespace Avalonia.Win32.DxgiSwapchain
public const uint DXGI_USAGE_RENDER_TARGET_OUTPUT = 0x00000020U;
- private IEglWindowGlPlatformSurfaceInfo _window;
- private EglPlatformOpenGlInterface _egl;
+ private EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window;
private DxgiConnection _connection;
private IDXGIDevice? _dxgiDevice = null;
private IDXGIFactory2? _dxgiFactory = null;
@@ -36,15 +36,14 @@ namespace Avalonia.Win32.DxgiSwapchain
private Guid ID3D11Texture2DGuid = Guid.Parse("6F15AAF2-D208-4E89-9AB4-489535D34F9C");
- public DxgiRenderTarget(IEglWindowGlPlatformSurfaceInfo window, EglPlatformOpenGlInterface egl, DxgiConnection connection) : base(egl)
+ public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window, EglContext context, DxgiConnection connection) : base(context)
{
_window = window;
- _egl = egl;
_connection = connection;
// the D3D device is expected to at least be an ID3D11Device
// but how do I wrap an IntPtr as a managed IUnknown now? Like this.
- IUnknown pdevice = MicroComRuntime.CreateProxyFor(((AngleWin32EglDisplay)_egl.Display).GetDirect3DDevice(), false);
+ IUnknown pdevice = MicroComRuntime.CreateProxyFor(((AngleWin32EglDisplay)context.Display).GetDirect3DDevice(), false);
_dxgiDevice = pdevice.QueryInterface();
@@ -86,14 +85,14 @@ namespace Avalonia.Win32.DxgiSwapchain
_clientRect = pClientRect;
}
- public override IGlPlatformSurfaceRenderingSession BeginDraw()
+ public override IGlPlatformSurfaceRenderingSession BeginDrawCore()
{
if (_swapChain is null)
{
throw new InvalidOperationException("No chain to draw on");
}
- var contextLock = _egl.PrimaryContext.EnsureCurrent();
+ var contextLock = Context.EnsureCurrent();
EglSurface? surface = null;
IDisposable? transaction = null;
var success = false;
@@ -132,10 +131,10 @@ namespace Avalonia.Win32.DxgiSwapchain
_renderTexture = texture;
// I also have to get the pointer to this texture directly
- surface = ((AngleWin32EglDisplay)_egl.Display).WrapDirect3D11Texture(_egl, MicroComRuntime.GetNativeIntPtr(_renderTexture),
+ surface = ((AngleWin32EglDisplay)Context.Display).WrapDirect3D11Texture(MicroComRuntime.GetNativeIntPtr(_renderTexture),
0, 0, size.Width, size.Height);
- var res = base.BeginDraw(surface, _window, () =>
+ var res = base.BeginDraw(surface, _window.Size, _window.Scaling, () =>
{
_swapChain.Present((ushort)0U, (ushort)0U);
surface?.Dispose();
diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs
index a40d8abf80..88226c5c89 100644
--- a/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs
+++ b/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs
@@ -3,29 +3,29 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
-namespace Avalonia.Win32.DxgiSwapchain
+namespace Avalonia.Win32.DirectX
{
public class DxgiSwapchainWindow : EglGlPlatformSurfaceBase
{
private DxgiConnection _connection;
- private EglPlatformOpenGlInterface _egl;
- private IEglWindowGlPlatformSurfaceInfo _window;
+ private EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window;
- public DxgiSwapchainWindow(DxgiConnection connection, IEglWindowGlPlatformSurfaceInfo window)
+ public DxgiSwapchainWindow(DxgiConnection connection, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window)
{
_connection = connection;
_window = window;
- _egl = connection.Egl;
}
- public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
- using (_egl.PrimaryContext.EnsureCurrent())
+ var eglContext = (EglContext)context;
+ using (eglContext.EnsureCurrent())
{
- return new DxgiRenderTarget(_window, _egl, _connection);
+ return new DxgiRenderTarget(_window, eglContext, _connection);
}
}
}
diff --git a/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs b/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs
new file mode 100644
index 0000000000..d2a2b2f513
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs
@@ -0,0 +1,26 @@
+using System;
+using Avalonia.OpenGL;
+using Avalonia.Platform;
+
+namespace Avalonia.Win32.DirectX;
+
+public interface IDirect3D11TexturePlatformSurface
+{
+ public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext graphicsContext, IntPtr d3dDevice);
+}
+
+
+
+public interface IDirect3D11TextureRenderTarget : IDisposable
+{
+ bool IsCorrupted { get; }
+ IDirect3D11TextureRenderTargetRenderSession BeginDraw();
+}
+
+public interface IDirect3D11TextureRenderTargetRenderSession : IDisposable
+{
+ public IntPtr D3D11Texture2D { get; }
+ public PixelSize Size { get; }
+ public PixelPoint Offset { get; }
+ public double Scaling { get; }
+}
diff --git a/src/Windows/Avalonia.Win32/DirectX/directx.idl b/src/Windows/Avalonia.Win32/DirectX/directx.idl
index 09d1adf574..a4552eedea 100644
--- a/src/Windows/Avalonia.Win32/DirectX/directx.idl
+++ b/src/Windows/Avalonia.Win32/DirectX/directx.idl
@@ -1,4 +1,4 @@
-@clr-namespace Avalonia.Win32.DxgiSwapchain
+@clr-namespace Avalonia.Win32.DirectX
@clr-access internal
@clr-map FLOAT float
@clr-map HSTRING IntPtr
@@ -12,6 +12,7 @@
@clr-map HWND IntPtr
@clr-map BOOL int
@clr-map DWORD int
+@clr-map SIZE_T IntPtr
@clr-map boolean int
@clr-map BYTE byte
@clr-map INT16 short
@@ -242,9 +243,9 @@ interface IDXGIOutput : IDXGIObject
HRESULT WaitForVBlank();
HRESULT TakeOwnership([in, annotation("_In_")] IUnknown* pDevice, BOOL Exclusive);
void ReleaseOwnership();
- HRESULT GetGammaControlCapabilities([out, annotation("_Out_")] DXGI_GAMMA_CONTROL_CAPABILITIES* pGammaCaps);
- HRESULT SetGammaControl([in, annotation("_In_")] DXGI_GAMMA_CONTROL* pArray);
- HRESULT GetGammaControl([out, annotation("_Out_")] DXGI_GAMMA_CONTROL* pArray);
+ HRESULT GetGammaControlCapabilities(IntPtr pGammaCaps);
+ HRESULT SetGammaControl([in, annotation("_In_")] void* pArray);
+ HRESULT GetGammaControl(IntPtr pArray);
HRESULT SetDisplaySurface([in, annotation("_In_")] IDXGISurface* pScanoutSurface);
HRESULT GetDisplaySurfaceData([in, annotation("_In_")] IDXGISurface* pDestination);
HRESULT GetFrameStatistics([out, annotation("_Out_")] DXGI_FRAME_STATISTICS* pStats);
@@ -303,3 +304,180 @@ interface IDXGISwapChain1 : IDXGISwapChain
HRESULT GetRotation([out, annotation("_Out_")] DXGI_MODE_ROTATION* pRotation);
}
+enum D3D11_FEATURE
+{
+ D3D11_FEATURE_THREADING,
+ D3D11_FEATURE_DOUBLES,
+ D3D11_FEATURE_FORMAT_SUPPORT,
+ D3D11_FEATURE_FORMAT_SUPPORT2,
+ D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS,
+ D3D11_FEATURE_D3D11_OPTIONS,
+ D3D11_FEATURE_ARCHITECTURE_INFO,
+ D3D11_FEATURE_D3D9_OPTIONS,
+ D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT,
+ D3D11_FEATURE_D3D9_SHADOW_SUPPORT,
+ D3D11_FEATURE_D3D11_OPTIONS1,
+ D3D11_FEATURE_D3D9_SIMPLE_INSTANCING_SUPPORT,
+ D3D11_FEATURE_MARKER_SUPPORT,
+ D3D11_FEATURE_D3D9_OPTIONS1,
+ D3D11_FEATURE_D3D11_OPTIONS2,
+ D3D11_FEATURE_D3D11_OPTIONS3,
+ D3D11_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT,
+ D3D11_FEATURE_D3D11_OPTIONSS,
+ D3D11_FEATURE_SHADER_CACHE
+}
+
+[uuid(db6f6ddb-ac77-4e88-8253-819df9bbf140)]
+interface ID3D11Device : IUnknown
+{
+ HRESULT CreateBuffer(
+ IntPtr pDesc,
+ IntPtr pInitialData,
+ [out, retval] IUnknown** ppBuffer );
+ HRESULT CreateTexture1D(
+ IntPtr pDesc,
+ IntPtr pInitialData,
+ [out, retval] IUnknown** ppTexture1D );
+ HRESULT CreateTexture2D(
+ IntPtr pDesc,
+ IntPtr pInitialData,
+ [out, retval] IUnknown** ppTexture2D );
+ HRESULT CreateTexture3D(
+ IntPtr pDesc,
+ IntPtr pInitialData,
+ [out, retval] IUnknown** ppTexture3D );
+ HRESULT CreateShaderResourceView(
+ IntPtr pResource,
+ IntPtr pDesc,
+ [out, retval] IUnknown** ppSRView );
+ HRESULT CreateUnorderedAccessView(
+ IntPtr pResource,
+ IntPtr pDesc,
+ [out, retval] IUnknown** ppUAView );
+ HRESULT CreateRenderTargetView(
+ IntPtr pResource,
+ IntPtr pDesc,
+ [out, retval] IUnknown** ppRTView);
+ HRESULT CreateDepthStencilView(
+ IntPtr pResource,
+ IntPtr pDesc,
+ [out, retval] IUnknown** ppDepthStencilView );
+ HRESULT CreateInputLayout(
+ IntPtr pInputElementDescs,
+ UINT NumElements,
+ void* pShaderBytecodeWithInputSignature,
+ IntPtr BytecodeLength,
+ [out, retval] IUnknown** ppInputLayout );
+ HRESULT CreateVertexShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppVertexShader );
+ HRESULT CreateGeometryShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppGeometryShader );
+ HRESULT CreateGeometryShaderWithStreamOutput(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pSODeclaration,
+ UINT NumEntries,
+ UINT* pBufferStrides,
+ UINT NumStrides,
+ UINT RasterizedStream,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppGeometryShader );
+ HRESULT CreatePixelShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppPixelShader );
+ HRESULT CreateHullShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppHullShader );
+ HRESULT CreateDomainShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppDomainShader );
+ HRESULT CreateComputeShader(
+ IntPtr pShaderBytecode,
+ [annotation("_In_")] SIZE_T BytecodeLength,
+ IntPtr pClassLinkage,
+ [out, retval] IUnknown** ppComputeShader );
+ HRESULT CreateClassLinkage([out, retval]IUnknown** ppLinkage);
+ HRESULT CreateBlendState(
+ IntPtr pBlendStateDesc,
+ [out, retval] IUnknown** ppBlendState );
+ HRESULT CreateDepthStencilState(
+ IntPtr pDepthStencilDesc,
+ [out, retval] IUnknown** ppDepthStencilState );
+ HRESULT CreateRasterizerState(
+ IntPtr pRasterizerDesc,
+ [out, retval] IUnknown** ppRasterizerState );
+ HRESULT CreateSamplerState(
+ IntPtr pSamplerDesc,
+ [out, retval] IUnknown** ppSamplerState );
+ HRESULT CreateQuery(
+ IntPtr pQueryDesc,
+ [out, retval] IUnknown** ppQuery );
+ HRESULT CreatePredicate(
+ IntPtr pPredicateDesc,
+ [out, retval] IUnknown** ppPredicate );
+ HRESULT CreateCounter(
+ IntPtr pCounterDesc,
+ [out, retval] IUnknown** ppCounter );
+ HRESULT CreateDeferredContext(
+ UINT ContextFlags, // Reserved parameter; must be 0
+ [out, retval] IUnknown** ppDeferredContext );
+ HRESULT OpenSharedResource(
+ IntPtr hResource,
+ [out] Guid* ReturnedInterface,
+ [out, retval] IUnknown** ppResource);
+
+ // Check*
+ HRESULT CheckFormatSupport(
+ [annotation("_In_")] DXGI_FORMAT Format,
+ [annotation("_Out_")] UINT* pFormatSupport );
+ HRESULT CheckMultisampleQualityLevels(
+ [annotation("_In_")] DXGI_FORMAT Format,
+ [annotation("_In_")] UINT SampleCount,
+ [annotation("_Out_")] UINT* pNumQualityLevels );
+ void CheckCounterInfo(IntPtr pCounterInfo );
+ HRESULT CheckCounter(
+ IntPtr pDesc,
+ IntPtr pType,
+ IntPtr pActiveCounters,
+ IntPtr szName,
+ UINT* pNameLength,
+ IntPtr szUnits,
+ UINT* pUnitsLength,
+ IntPtr szDescription,
+ UINT* pDescriptionLength );
+ HRESULT CheckFeatureSupport(
+ D3D11_FEATURE Feature,
+ void* pFeatureSupportData,
+ UINT FeatureSupportDataSize );
+
+ HRESULT GetPrivateData(
+ Guid* guid,
+ UINT* pDataSize,
+ void* pData );
+ HRESULT SetPrivateData(
+ Guid* guid,
+ UINT DataSize,
+ IntPtr* pData );
+ HRESULT SetPrivateDataInterface(
+ Guid* guid,
+ IUnknown* pData );
+
+ D3D_FEATURE_LEVEL GetFeatureLevel();
+ UINT GetCreationFlags();
+ int GetDeviceRemovedReason();
+ void GetImmediateContext(IntPtr* ppImmediateContext );
+ HRESULT SetExceptionMode( UINT RaiseFlags );
+ UINT GetExceptionMode();
+}
diff --git a/src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs b/src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs
index 966f996a91..08b3ee32fa 100644
--- a/src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs
+++ b/src/Windows/Avalonia.Win32/Interop/Automation/UiaCoreTypesApi.cs
@@ -6,8 +6,6 @@ namespace Avalonia.Win32.Interop.Automation
{
internal static class UiaCoreTypesApi
{
- private const string StartListeningExportName = "SynchronizedInputPattern_StartListening";
-
internal enum AutomationIdType
{
Property,
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index 9b86154043..672f3a781a 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -820,6 +820,14 @@ namespace Avalonia.Win32.Interop
DWMWA_LAST
};
+ public enum DwmWindowCornerPreference : uint
+ {
+ DWMWCP_DEFAULT = 0,
+ DWMWCP_DONOTROUND,
+ DWMWCP_ROUND,
+ DWMWCP_ROUNDSMALL
+ }
+
public enum MapVirtualKeyMapTypes : uint
{
MAPVK_VK_TO_VSC = 0x00,
@@ -1409,7 +1417,7 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")]
public static extern bool TranslateMessage(ref MSG lpMsg);
- [DllImport("user32.dll")]
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")]
@@ -1627,7 +1635,7 @@ namespace Avalonia.Win32.Interop
public static extern bool wglDeleteContext(IntPtr context);
- [DllImport("opengl32.dll")]
+ [DllImport("opengl32.dll", SetLastError = true)]
public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context);
[DllImport("opengl32.dll")]
diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs
new file mode 100644
index 0000000000..6c951010c8
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs
@@ -0,0 +1,103 @@
+using System.Runtime.InteropServices;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Surfaces;
+using Avalonia.Win32.DirectX;
+
+namespace Avalonia.Win32.OpenGl.Angle;
+
+internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory
+{
+ public bool CanRenderToSurface(IGlContext context, object surface) =>
+ context is EglContext
+ {
+ Display: AngleWin32EglDisplay { PlatformApi: AngleOptions.PlatformApi.DirectX11 }
+ } && surface is IDirect3D11TexturePlatformSurface;
+
+ class RenderTargetWrapper : EglPlatformSurfaceRenderTargetBase
+ {
+ private readonly AngleWin32EglDisplay _angle;
+ private readonly IDirect3D11TextureRenderTarget _target;
+
+ public RenderTargetWrapper(EglContext context,
+ AngleWin32EglDisplay angle,
+ IDirect3D11TextureRenderTarget target) : base(context)
+ {
+ _angle = angle;
+ _target = target;
+ }
+
+ public override IGlPlatformSurfaceRenderingSession BeginDrawCore()
+ {
+ var success = false;
+ var contextLock = Context.EnsureCurrent();
+ IDirect3D11TextureRenderTargetRenderSession session = null;
+ EglSurface surface = null;
+ try
+ {
+ try
+ {
+ session = _target.BeginDraw();
+ }
+ catch (RenderTargetCorruptedException e)
+ {
+ if (e.InnerException is COMException com
+ && ((DXGI_ERROR)com.HResult).IsDeviceLostError())
+ Context.NotifyContextLost();
+
+ throw;
+ }
+
+ surface = _angle.WrapDirect3D11Texture(session.D3D11Texture2D, session.Offset.X, session.Offset.Y,
+ session.Size.Width, session.Size.Height);
+ var rv = BeginDraw(surface, session.Size, session.Scaling, () =>
+ {
+ using(contextLock)
+ using (session)
+ using (surface)
+ {
+ }
+ }, true);
+ success = true;
+ return rv;
+ }
+ finally
+ {
+ if (!success)
+ {
+ using(contextLock)
+ using (session)
+ using (surface)
+ {
+ }
+ }
+ }
+ }
+
+ public override void Dispose()
+ {
+ _target.Dispose();
+ base.Dispose();
+ }
+
+ public override bool IsCorrupted => _target.IsCorrupted || base.IsCorrupted;
+ }
+
+ public IGlPlatformSurfaceRenderTarget CreateRenderTarget(IGlContext context, object surface)
+ {
+ var ctx = (EglContext)context;
+ var angle = (AngleWin32EglDisplay)ctx.Display;
+ var textureSurface = (IDirect3D11TexturePlatformSurface)surface;
+ try
+ {
+ var target = textureSurface.CreateRenderTarget(context, angle.GetDirect3DDevice());
+ return new RenderTargetWrapper(ctx, angle, target);
+ }
+ catch (COMException com)
+ {
+ if (((DXGI_ERROR)com.HResult).IsDeviceLostError())
+ ctx.NotifyContextLost();
+ throw;
+ }
+ }
+}
diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleEglInterface.cs
similarity index 56%
rename from src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
rename to src/Windows/Avalonia.Win32/OpenGl/Angle/AngleEglInterface.cs
index 3a332f37ad..4e00bc2c72 100644
--- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleEglInterface.cs
@@ -1,18 +1,31 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
+using Avalonia.SourceGenerator;
namespace Avalonia.OpenGL.Angle
{
- public class AngleEglInterface : EglInterface
+ internal partial class Win32AngleEglInterface : EglInterface
{
[DllImport("av_libGLESv2.dll", CharSet = CharSet.Ansi)]
static extern IntPtr EGL_GetProcAddress(string proc);
- public AngleEglInterface() : base(LoadAngle())
- {
+ public Win32AngleEglInterface() : this(LoadAngle())
+ {
+
}
+
+ private Win32AngleEglInterface(Func getProcAddress) : base(getProcAddress)
+ {
+ Initialize(getProcAddress);
+ }
+
+ [GetProcAddress("eglCreateDeviceANGLE", true)]
+ public partial IntPtr CreateDeviceANGLE(int deviceType, IntPtr nativeDevice, int[] attribs);
+
+ [GetProcAddress("eglReleaseDeviceANGLE", true)]
+ public partial void ReleaseDeviceANGLE(IntPtr device);
static Func LoadAngle()
{
diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32EglDisplay.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32EglDisplay.cs
new file mode 100644
index 0000000000..b2d7b2014b
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32EglDisplay.cs
@@ -0,0 +1,140 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
+using Avalonia.OpenGL.Egl;
+using Avalonia.Win32.DirectX;
+using MicroCom.Runtime;
+using static Avalonia.OpenGL.Egl.EglConsts;
+
+namespace Avalonia.Win32.OpenGl.Angle
+{
+ internal class AngleWin32EglDisplay : EglDisplay
+ {
+ protected override bool DisplayLockIsSharedWithContexts => true;
+
+ public static AngleWin32EglDisplay CreateD3D9Display(EglInterface egl)
+ {
+ var display = egl.GetPlatformDisplayExt(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
+ new[] { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, EGL_NONE });
+
+ return new AngleWin32EglDisplay(display, new EglDisplayOptions()
+ {
+ Egl = egl,
+ ContextLossIsDisplayLoss = true,
+ GlVersions = AvaloniaLocator.Current.GetService()?.GlProfiles
+ }, AngleOptions.PlatformApi.DirectX9);
+ }
+
+ public static AngleWin32EglDisplay CreateSharedD3D11Display(EglInterface egl)
+ {
+ var display = egl.GetPlatformDisplayExt(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
+ new[] { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE });
+
+ return new AngleWin32EglDisplay(display, new EglDisplayOptions()
+ {
+ Egl = egl,
+ ContextLossIsDisplayLoss = true,
+ GlVersions = AvaloniaLocator.Current.GetService()?.GlProfiles
+ }, AngleOptions.PlatformApi.DirectX11);
+ }
+
+ public static AngleWin32EglDisplay CreateD3D11Display(Win32AngleEglInterface egl)
+ {
+ unsafe
+ {
+ var featureLevels = new[]
+ {
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_9_1
+ };
+
+ DirectXUnmanagedMethods.D3D11CreateDevice(IntPtr.Zero, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE,
+ IntPtr.Zero, 0, featureLevels, (uint)featureLevels.Length,
+ 7, out var pD3dDevice, out var featureLevel, null);
+ if (pD3dDevice == IntPtr.Zero)
+ throw new Win32Exception("Unable to create D3D11 Device");
+
+ var d3dDevice = MicroComRuntime.CreateProxyFor(pD3dDevice, true);
+ var angleDevice = IntPtr.Zero;
+ var display = IntPtr.Zero;
+
+ void Cleanup()
+ {
+ if (angleDevice != IntPtr.Zero)
+ egl.ReleaseDeviceANGLE(angleDevice);
+ d3dDevice.Dispose();
+ }
+
+ bool success = false;
+ try
+ {
+ angleDevice = egl.CreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, pD3dDevice, null);
+ if (angleDevice == IntPtr.Zero)
+ throw OpenGlException.GetFormattedException("eglCreateDeviceANGLE", egl);
+
+ display = egl.GetPlatformDisplayExt(EGL_PLATFORM_DEVICE_EXT, angleDevice, null);
+ if (display == IntPtr.Zero)
+ throw OpenGlException.GetFormattedException("eglGetPlatformDisplayEXT", egl);
+
+
+ var rv = new AngleWin32EglDisplay(display, new EglDisplayOptions
+ {
+ DisposeCallback = Cleanup,
+ Egl = egl,
+ ContextLossIsDisplayLoss = true,
+ DeviceLostCheckCallback = () => d3dDevice.DeviceRemovedReason != 0,
+ GlVersions = AvaloniaLocator.Current.GetService()?.GlProfiles
+ }, AngleOptions.PlatformApi.DirectX11);
+ success = true;
+ return rv;
+ }
+ finally
+ {
+ if (!success)
+ {
+ if (display != IntPtr.Zero)
+ egl.Terminate(display);
+ Cleanup();
+ }
+ }
+ }
+ }
+
+ private AngleWin32EglDisplay(IntPtr display, EglDisplayOptions options, AngleOptions.PlatformApi platformApi) : base(display, options)
+ {
+ PlatformApi = platformApi;
+ }
+
+ public AngleOptions.PlatformApi PlatformApi { get; }
+
+ public IntPtr GetDirect3DDevice()
+ {
+ if (!EglInterface.QueryDisplayAttribExt(Handle, EglConsts.EGL_DEVICE_EXT, out var eglDevice))
+ throw new OpenGlException("Unable to get EGL_DEVICE_EXT");
+ if (!EglInterface.QueryDeviceAttribExt(eglDevice, PlatformApi == AngleOptions.PlatformApi.DirectX9 ? EGL_D3D9_DEVICE_ANGLE : EGL_D3D11_DEVICE_ANGLE, out var d3dDeviceHandle))
+ throw new OpenGlException("Unable to get EGL_D3D9_DEVICE_ANGLE");
+ return d3dDeviceHandle;
+ }
+
+ public EglSurface WrapDirect3D11Texture( IntPtr handle)
+ {
+ if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
+ throw new InvalidOperationException("Current platform API is " + PlatformApi);
+ return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ }
+
+ public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY, int width, int height)
+ {
+ if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
+ throw new InvalidOperationException("Current platform API is " + PlatformApi);
+ return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE });
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
new file mode 100644
index 0000000000..9a829aff92
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+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
+{
+ private readonly Win32AngleEglInterface _egl;
+ private AngleWin32EglDisplay _sharedDisplay;
+ private EglContext _sharedContext;
+ public bool UsesSharedContext => PlatformApi == AngleOptions.PlatformApi.DirectX9;
+
+ public AngleOptions.PlatformApi PlatformApi { get; } = AngleOptions.PlatformApi.DirectX11;
+ 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()
+ }
+ });
+ 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;
+ }
+}
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
index d6633ddb61..d535efc5b7 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
@@ -1,6 +1,10 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Reactive.Disposables;
+using System.Threading;
using Avalonia.OpenGL;
+using Avalonia.Platform;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts;
@@ -43,8 +47,9 @@ namespace Avalonia.Win32.OpenGl
public void Dispose()
{
wglDeleteContext(_context);
- ReleaseDC(_hWnd, _dc);
- DestroyWindow(_hWnd);
+ WglGdiResourceManager.ReleaseDC(_hWnd, _dc);
+ WglGdiResourceManager.DestroyWindow(_hWnd);
+ IsLost = true;
}
public GlVersion Version { get; }
@@ -55,17 +60,26 @@ namespace Avalonia.Win32.OpenGl
private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc;
public IDisposable MakeCurrent()
{
+ if (IsLost)
+ throw new PlatformGraphicsContextLostException();
if(IsCurrent)
return Disposable.Empty;
return new WglRestoreContext(_dc, _context, _lock);
}
+ public bool IsLost { get; private set; }
public IDisposable EnsureCurrent() => MakeCurrent();
+ internal IDisposable Lock()
+ {
+ Monitor.Enter(_lock);
+ return Disposable.Create(_lock, Monitor.Exit);
+ }
+
public IntPtr CreateConfiguredDeviceContext(IntPtr hWnd)
{
- var dc = GetDC(hWnd);
+ var dc = WglGdiResourceManager.GetDC(hWnd);
var fmt = _formatDescriptor;
SetPixelFormat(dc, _pixelFormat, ref fmt);
return dc;
@@ -81,5 +95,14 @@ namespace Avalonia.Win32.OpenGl
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
+
+ public bool CanCreateSharedContext => true;
+ public IGlContext CreateSharedContext(IEnumerable preferredVersions = null)
+ {
+ var versions = preferredVersions?.Append(Version).ToArray() ?? new[] { Version };
+ return WglDisplay.CreateContext(versions, _sharedWith ?? this);
+ }
+
+ public object TryGetFeature(Type featureType) => null;
}
}
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
index bc27589689..9a1963a97a 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL;
+using Avalonia.Threading;
using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts;
@@ -9,8 +10,6 @@ namespace Avalonia.Win32.OpenGl
internal class WglDisplay
{
private static bool? _initialized;
- private static ushort _windowClass;
- private static readonly WndProc _wndProcDelegate = WndProc;
private static readonly DebugCallbackDelegate _debugCallback = DebugCallback;
private static IntPtr _bootstrapContext;
@@ -44,18 +43,9 @@ namespace Avalonia.Win32.OpenGl
}
static bool InitializeCore()
{
- var wndClassEx = new WNDCLASSEX
- {
- cbSize = Marshal.SizeOf(),
- hInstance = GetModuleHandle(null),
- lpfnWndProc = _wndProcDelegate,
- lpszClassName = "AvaloniaGlWindow-" + Guid.NewGuid(),
- style = (int)ClassStyles.CS_OWNDC
- };
-
- _windowClass = RegisterClassEx(ref wndClassEx);
- _bootstrapWindow = CreateOffscreenWindow();
- _bootstrapDc = GetDC(_bootstrapWindow);
+ Dispatcher.UIThread.VerifyAccess();
+ _bootstrapWindow = WglGdiResourceManager.CreateOffscreenWindow();
+ _bootstrapDc = WglGdiResourceManager.GetDC(_bootstrapWindow);
_defaultPfd = new PixelFormatDescriptor
{
Size = (ushort)Marshal.SizeOf(),
@@ -105,17 +95,11 @@ namespace Avalonia.Win32.OpenGl
DescribePixelFormat(_bootstrapDc, formats[0], Marshal.SizeOf(), ref _defaultPfd);
_defaultPixelFormat = formats[0];
}
-
-
+
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
return true;
}
-
- static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
- {
- return DefWindowProc(hWnd, msg, wParam, lParam);
- }
-
+
private static void DebugCallback(int source, int type, int id, int severity, int len, IntPtr message, IntPtr userparam)
{
var err = Marshal.PtrToStringAnsi(message, len);
@@ -131,27 +115,32 @@ namespace Avalonia.Win32.OpenGl
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null))
{
- var window = CreateOffscreenWindow();
- var dc = GetDC(window);
+ var window = WglGdiResourceManager.CreateOffscreenWindow();
+ var dc = WglGdiResourceManager.GetDC(window);
SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd);
foreach (var version in versions)
{
if(version.Type != GlProfileType.OpenGL)
continue;
- var context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero,
- new[]
- {
- // major
- WGL_CONTEXT_MAJOR_VERSION_ARB, version.Major,
- // minor
- WGL_CONTEXT_MINOR_VERSION_ARB, version.Minor,
- // core profile
- WGL_CONTEXT_PROFILE_MASK_ARB, 1,
- // debug
- // WGL_CONTEXT_FLAGS_ARB, 1,
- // end
- 0, 0
- });
+ IntPtr context;
+ using (shareContext?.Lock())
+ {
+ context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero,
+ new[]
+ {
+ // major
+ WGL_CONTEXT_MAJOR_VERSION_ARB, version.Major,
+ // minor
+ WGL_CONTEXT_MINOR_VERSION_ARB, version.Minor,
+ // core profile
+ WGL_CONTEXT_PROFILE_MASK_ARB, 1,
+ // debug
+ // WGL_CONTEXT_FLAGS_ARB, 1,
+ // end
+ 0, 0
+ });
+ }
+
using(new WglRestoreContext(dc, context, null))
GlDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero);
if (context != IntPtr.Zero)
@@ -159,26 +148,13 @@ namespace Avalonia.Win32.OpenGl
_defaultPixelFormat, _defaultPfd);
}
- ReleaseDC(window, dc);
- DestroyWindow(window);
+ WglGdiResourceManager.ReleaseDC(window, dc);
+ WglGdiResourceManager.DestroyWindow(window);
return null;
}
}
- static IntPtr CreateOffscreenWindow() =>
- CreateWindowEx(
- 0,
- _windowClass,
- null,
- (int)WindowStyles.WS_OVERLAPPEDWINDOW,
- 0,
- 0,
- 640,
- 480,
- IntPtr.Zero,
- IntPtr.Zero,
- IntPtr.Zero,
- IntPtr.Zero);
+
}
}
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglGdiResourceManager.cs b/src/Windows/Avalonia.Win32/OpenGl/WglGdiResourceManager.cs
new file mode 100644
index 0000000000..ed40488c70
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglGdiResourceManager.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Win32.Interop;
+
+namespace Avalonia.Win32.OpenGl;
+
+///
+/// - ReleaseDC can only happen from the same thread that has called GetDC
+/// - When thread exits all of its windows and HDCs are getting destroyed
+/// - We need to create OpenGL context (require a window and an HDC) and render targets (require an HDC) from thread pool threads
+///
+/// So this class hosts a dedicated thread for managing offscreen windows and HDCs for OpenGL
+///
+
+internal class WglGdiResourceManager
+{
+ class GetDCOp
+ {
+ public IntPtr Window;
+ public TaskCompletionSource Result;
+ }
+
+ class ReleaseDCOp
+ {
+ public IntPtr Window;
+ public IntPtr DC;
+ public TaskCompletionSource Result;
+ }
+
+ class CreateWindowOp
+ {
+ public TaskCompletionSource Result;
+ }
+
+ class DestroyWindowOp
+ {
+ public IntPtr Window;
+ public TaskCompletionSource Result;
+ }
+
+ private static readonly Queue s_Queue = new();
+ private static readonly AutoResetEvent s_Event = new(false);
+ private static readonly ushort s_WindowClass;
+ private static readonly UnmanagedMethods.WndProc s_wndProcDelegate = WndProc;
+
+ static void Worker()
+ {
+ while (true)
+ {
+ s_Event.WaitOne();
+ lock (s_Queue)
+ {
+ if(s_Queue.Count == 0)
+ continue;
+ var job = s_Queue.Dequeue();
+ if (job is GetDCOp getDc)
+ getDc.Result.TrySetResult(UnmanagedMethods.GetDC(getDc.Window));
+ else if (job is ReleaseDCOp releaseDc)
+ {
+ UnmanagedMethods.ReleaseDC(releaseDc.Window, releaseDc.DC);
+ releaseDc.Result.SetResult(null);
+ }
+ else if (job is CreateWindowOp createWindow)
+ createWindow.Result.TrySetResult(UnmanagedMethods.CreateWindowEx(
+ 0,
+ s_WindowClass,
+ null,
+ (int)UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW,
+ 0,
+ 0,
+ 640,
+ 480,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ IntPtr.Zero));
+ else if (job is DestroyWindowOp destroyWindow)
+ {
+ UnmanagedMethods.DestroyWindow(destroyWindow.Window);
+ destroyWindow.Result.TrySetResult(null);
+ }
+ }
+ }
+ }
+
+ static WglGdiResourceManager()
+ {
+ var wndClassEx = new UnmanagedMethods.WNDCLASSEX
+ {
+ cbSize = Marshal.SizeOf(),
+ hInstance = UnmanagedMethods.GetModuleHandle(null),
+ lpfnWndProc = s_wndProcDelegate,
+ lpszClassName = "AvaloniaGlWindow-" + Guid.NewGuid(),
+ style = (int)UnmanagedMethods.ClassStyles.CS_OWNDC
+ };
+
+ s_WindowClass = UnmanagedMethods.RegisterClassEx(ref wndClassEx);
+ var th = new Thread(Worker) { IsBackground = true, Name = "Win32 OpenGL HDC manager" };
+ // This makes CLR to automatically pump the event queue from WaitOne
+ th.SetApartmentState(ApartmentState.STA);
+ th.Start();
+ }
+
+
+
+ static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
+ {
+ return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
+ }
+
+ public static IntPtr CreateOffscreenWindow()
+ {
+ var tcs = new TaskCompletionSource();
+ lock(s_Queue)
+ s_Queue.Enqueue(new CreateWindowOp()
+ {
+ Result = tcs
+ });
+ s_Event.Set();
+ return tcs.Task.Result;
+ }
+
+ public static IntPtr GetDC(IntPtr hWnd)
+ {
+ var tcs = new TaskCompletionSource();
+ lock(s_Queue)
+ s_Queue.Enqueue(new GetDCOp
+ {
+ Window = hWnd,
+ Result = tcs
+ });
+ s_Event.Set();
+ return tcs.Task.Result;
+ }
+
+ public static void ReleaseDC(IntPtr hWnd, IntPtr hDC)
+ {
+ var tcs = new TaskCompletionSource();
+ lock(s_Queue)
+ s_Queue.Enqueue(new ReleaseDCOp()
+ {
+ Window = hWnd,
+ DC = hDC,
+ Result = tcs
+ });
+ s_Event.Set();
+ tcs.Task.Wait();
+ }
+
+ public static void DestroyWindow(IntPtr hWnd)
+ {
+ var tcs = new TaskCompletionSource();
+ lock(s_Queue)
+ s_Queue.Enqueue(new DestroyWindowOp()
+ {
+ Window = hWnd,
+ Result = tcs
+ });
+ s_Event.Set();
+ tcs.Task.Wait();
+
+ }
+}
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs
index 72bcffd447..6d3d2d28c4 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs
@@ -11,18 +11,16 @@ namespace Avalonia.Win32.OpenGl
class WglGlPlatformSurface: IGlPlatformSurface
{
- private readonly WglContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
- public WglGlPlatformSurface(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
+ public WglGlPlatformSurface( EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
- _context = context;
_info = info;
}
- public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
- return new RenderTarget(_context, _info);
+ return new RenderTarget((WglContext)context, _info);
}
class RenderTarget : IGlPlatformSurfaceRenderTarget
@@ -39,7 +37,7 @@ namespace Avalonia.Win32.OpenGl
public void Dispose()
{
- UnmanagedMethods.ReleaseDC(_hdc, _info.Handle);
+ WglGdiResourceManager.ReleaseDC(_info.Handle, _hdc);
}
public IGlPlatformSurfaceRenderingSession BeginDraw()
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs
index 1d0880a468..39dd330d52 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs
@@ -6,15 +6,13 @@ using Avalonia.Platform;
namespace Avalonia.Win32.OpenGl
{
- class WglPlatformOpenGlInterface : IPlatformOpenGlInterface
+ class WglPlatformOpenGlInterface : IPlatformGraphics
{
public WglContext PrimaryContext { get; }
- IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext;
- IGlContext IPlatformOpenGlInterface.PrimaryContext => PrimaryContext;
- public IGlContext CreateSharedContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, PrimaryContext);
-
- public bool CanShareContexts => true;
- public bool CanCreateContexts => true;
+ public bool UsesSharedContext => false;
+ IPlatformGraphicsContext IPlatformGraphics.CreateContext() => CreateContext();
+ public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
+
public IGlContext CreateContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null);
private WglPlatformOpenGlInterface(WglContext primary)
diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs b/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs
index 265f078a5c..b145ffbb37 100644
--- a/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs
+++ b/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.OpenGL;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@@ -22,9 +23,11 @@ namespace Avalonia.Win32.OpenGl
if (!wglMakeCurrent(gc, context))
{
+ var lastError = Marshal.GetLastWin32Error();
+ var caps = GetDeviceCaps(gc, (DEVICECAP)12);
if(monitor != null && takeMonitor)
Monitor.Exit(monitor);
- throw new OpenGlException("Unable to make the context current");
+ throw new OpenGlException($"Unable to make the context current: {lastError}, DC valid: {caps != 0}");
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index 204acc82c9..25ea060576 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -2,8 +2,9 @@ using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
-using Avalonia.Win32.DxgiSwapchain;
+using Avalonia.Win32.DirectX;
using Avalonia.Win32.OpenGl;
+using Avalonia.Win32.OpenGl.Angle;
using Avalonia.Win32.WinRT.Composition;
namespace Avalonia.Win32
@@ -11,15 +12,15 @@ namespace Avalonia.Win32
static class Win32GlManager
{
- public static IPlatformOpenGlInterface Initialize()
+ public static IPlatformGraphics Initialize()
{
var gl = InitializeCore();
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(gl);
return gl;
}
- static IPlatformOpenGlInterface InitializeCore()
+ static IPlatformGraphics InitializeCore()
{
var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions();
@@ -31,28 +32,18 @@ namespace Avalonia.Win32
if (opts.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7)
{
- var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
+ var egl = AngleWin32PlatformGraphics.TryCreate(AvaloniaLocator.Current.GetService() ??
+ new());
- if (egl != null)
+ if (egl != null && egl.PlatformApi == AngleOptions.PlatformApi.DirectX11)
{
- if (opts.EglRendererBlacklist != null)
- {
- foreach (var item in opts.EglRendererBlacklist)
- {
- if (egl.PrimaryEglContext.GlInterface.Renderer.Contains(item))
- {
- return null;
- }
- }
- }
-
if (opts.UseWindowsUIComposition)
{
- WinUICompositorConnection.TryCreateAndRegister(egl, opts.CompositionBackdropCornerRadius);
+ WinUiCompositorConnection.TryCreateAndRegister();
}
else if (opts.UseLowLatencyDxgiSwapChain)
{
- DxgiConnection.TryCreateAndRegister(egl);
+ DxgiConnection.TryCreateAndRegister();
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 63cad9679a..3f16b49772 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -23,6 +23,7 @@ using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia
{
+#nullable enable
public static class Win32ApplicationExtensions
{
public static T UseWin32(
@@ -59,11 +60,6 @@ namespace Avalonia
/// GPU rendering will not be enabled if this is set to false.
///
public bool? AllowEglInitialization { get; set; }
-
- public IList EglRendererBlacklist { get; set; } = new List
- {
- "Microsoft Basic Render"
- };
///
/// Embeds popups to the window when set to true. The default value is false.
@@ -106,8 +102,14 @@ namespace Avalonia
/// which if active will override this setting.
///
public bool UseLowLatencyDxgiSwapChain { get; set; } = false;
+
+ ///
+ /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu
+ ///
+ public IPlatformGraphics? CustomPlatformGraphics { get; set; }
}
}
+#nullable restore
namespace Avalonia.Win32
{
@@ -139,6 +141,7 @@ namespace Avalonia.Win32
public static Win32PlatformOptions Options { get; private set; }
internal static Compositor Compositor { get; private set; }
+ internal static PlatformRenderInterfaceContextManager RenderInterface { get; private set; }
public static void Initialize()
{
@@ -169,16 +172,19 @@ namespace Avalonia.Win32
.Bind().ToConstant(new NonPumpingSyncContext.HelperImpl())
.Bind().ToConstant(new WindowsMountedVolumeInfoProvider())
.Bind().ToConstant(s_instance);
-
- var gl = Win32GlManager.Initialize();
-
+
_uiThread = Thread.CurrentThread;
+ var platformGraphics = options?.CustomPlatformGraphics
+ ?? Win32GlManager.Initialize();
+
if (OleContext.Current != null)
AvaloniaLocator.CurrentMutable.Bind().ToSingleton();
if (Options.UseCompositor)
- Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), gl);
+ Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), platformGraphics);
+ else
+ RenderInterface = new PlatformRenderInterfaceContextManager(platformGraphics);
}
public bool HasMessages()
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
deleted file mode 100644
index ef3de9fbe1..0000000000
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System;
-using System.Numerics;
-using System.Reactive.Disposables;
-using System.Threading;
-using Avalonia.MicroCom;
-using Avalonia.OpenGL;
-using Avalonia.OpenGL.Egl;
-using Avalonia.Win32.Interop;
-using MicroCom.Runtime;
-
-namespace Avalonia.Win32.WinRT.Composition
-{
- public class WinUICompositedWindow : IDisposable
- {
- private EglContext _syncContext;
- private readonly object _pumpLock;
- private readonly IVisual _micaVisual;
- private readonly ICompositionRoundedRectangleGeometry _roundedRectangleGeometry;
- private readonly IVisual _blurVisual;
- private ICompositionTarget _compositionTarget;
- private IVisual _contentVisual;
- private ICompositionDrawingSurfaceInterop _surfaceInterop;
- private PixelSize _size;
-
- private static Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
- private ICompositor _compositor;
-
-
- internal WinUICompositedWindow(EglContext syncContext,
- ICompositor compositor,
- object pumpLock,
- ICompositionTarget compositionTarget,
- ICompositionDrawingSurfaceInterop surfaceInterop,
- IVisual contentVisual, IVisual blurVisual, IVisual micaVisual,
- ICompositionRoundedRectangleGeometry roundedRectangleGeometry)
- {
- _compositor = compositor.CloneReference();
- _syncContext = syncContext;
- _pumpLock = pumpLock;
- _micaVisual = micaVisual;
- _roundedRectangleGeometry = roundedRectangleGeometry;
- _blurVisual = blurVisual.CloneReference();
- _compositionTarget = compositionTarget.CloneReference();
- _contentVisual = contentVisual.CloneReference();
- _surfaceInterop = surfaceInterop.CloneReference();
- }
-
-
- public void ResizeIfNeeded(PixelSize size)
- {
- using (_syncContext.EnsureLocked())
- {
- if (_size != size)
- {
- _surfaceInterop.Resize(new UnmanagedMethods.POINT { X = size.Width, Y = size.Height });
- _contentVisual.SetSize(new Vector2(size.Width, size.Height));
- _roundedRectangleGeometry?.SetSize(new Vector2(size.Width, size.Height));
- _size = size;
- }
- }
- }
-
- public unsafe IUnknown BeginDrawToTexture(out PixelPoint offset)
- {
- if (!_syncContext.IsCurrent)
- throw new InvalidOperationException();
-
- var iid = IID_ID3D11Texture2D;
- void* pTexture;
- var off = _surfaceInterop.BeginDraw(null, &iid, &pTexture);
- offset = new PixelPoint(off.X, off.Y);
- return MicroComRuntime.CreateProxyFor(pTexture, true);
- }
-
- public void EndDraw()
- {
- if (!_syncContext.IsCurrent)
- throw new InvalidOperationException();
- _surfaceInterop.EndDraw();
- }
-
- public void SetBlur(BlurEffect blurEffect)
- {
- using (_syncContext.EnsureLocked())
- {
- _blurVisual.SetIsVisible(blurEffect == BlurEffect.Acrylic ? 1 : 0);
- _micaVisual?.SetIsVisible(blurEffect == BlurEffect.Mica ? 1 : 0);
- }
- }
-
- public IDisposable BeginTransaction()
- {
- Monitor.Enter(_pumpLock);
- return Disposable.Create(() => Monitor.Exit(_pumpLock));
- }
-
- public void Dispose()
- {
- if (_syncContext == null)
- {
- _compositor.Dispose();
- _blurVisual.Dispose();
- _contentVisual.Dispose();
- _surfaceInterop.Dispose();
- _compositionTarget.Dispose();
- }
- }
- }
-}
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
deleted file mode 100644
index 6da17b8ea5..0000000000
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
+++ /dev/null
@@ -1,311 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Numerics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Avalonia.Logging;
-using Avalonia.MicroCom;
-using Avalonia.OpenGL;
-using Avalonia.OpenGL.Angle;
-using Avalonia.OpenGL.Egl;
-using Avalonia.Rendering;
-using Avalonia.Win32.Interop;
-using MicroCom.Runtime;
-
-namespace Avalonia.Win32.WinRT.Composition
-{
- class WinUICompositorConnection : IRenderTimer
- {
- public static readonly Version MinHostBackdropVersion = new Version(10, 0, 22000);
- private readonly float? _backdropCornerRadius;
- private readonly EglContext _syncContext;
- private readonly ICompositionBrush _micaBrush;
- private ICompositor _compositor;
- private ICompositor5 _compositor5;
- private ICompositorInterop _compositorInterop;
- private AngleWin32EglDisplay _angle;
- private ICompositionGraphicsDevice _device;
- private EglPlatformOpenGlInterface _gl;
- private ICompositorDesktopInterop _compositorDesktopInterop;
- private ICompositionBrush _blurBrush;
- private object _pumpLock = new object();
-
- public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock, float? backdropCornerRadius)
- {
- _gl = gl;
- _pumpLock = pumpLock;
- _backdropCornerRadius = backdropCornerRadius;
- _syncContext = _gl.PrimaryEglContext;
- _angle = (AngleWin32EglDisplay)_gl.Display;
- _compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor");
- _compositor5 = _compositor.QueryInterface();
- _compositorInterop = _compositor.QueryInterface();
- _compositorDesktopInterop = _compositor.QueryInterface();
- using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true);
-
- _device = _compositorInterop.CreateGraphicsDevice(device);
- _blurBrush = CreateAcrylicBlurBackdropBrush();
- _micaBrush = CreateMicaBackdropBrush();
- }
-
- public EglPlatformOpenGlInterface Egl => _gl;
-
- static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle, float? backdropCornerRadius)
- {
- var tcs = new TaskCompletionSource();
- var pumpLock = new object();
- var th = new Thread(() =>
- {
- WinUICompositorConnection connect;
- try
- {
- NativeWinRTMethods.CreateDispatcherQueueController(new NativeWinRTMethods.DispatcherQueueOptions
- {
- apartmentType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE,
- dwSize = Marshal.SizeOf(),
- threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT
- });
- connect = new WinUICompositorConnection(angle, pumpLock, backdropCornerRadius);
- AvaloniaLocator.CurrentMutable.BindToSelf(connect);
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect);
- tcs.SetResult(true);
-
- }
- catch (Exception e)
- {
- tcs.SetException(e);
- return;
- }
- connect.RunLoop();
- })
- {
- IsBackground = true
- };
- th.SetApartmentState(ApartmentState.STA);
- th.Start();
- return tcs.Task.Result;
- }
-
- class RunLoopHandler : IAsyncActionCompletedHandler, IMicroComShadowContainer
- {
- private readonly WinUICompositorConnection _parent;
- private Stopwatch _st = Stopwatch.StartNew();
-
- public RunLoopHandler(WinUICompositorConnection parent)
- {
- _parent = parent;
- }
- public void Dispose()
- {
-
- }
-
- public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus)
- {
- _parent.Tick?.Invoke(_st.Elapsed);
- using var act = _parent._compositor5.RequestCommitAsync();
- act.SetCompleted(this);
- }
-
- public MicroComShadow Shadow { get; set; }
- public void OnReferencedFromNative()
- {
- }
-
- public void OnUnreferencedFromNative()
- {
- }
- }
-
- private void RunLoop()
- {
- var cts = new CancellationTokenSource();
- AppDomain.CurrentDomain.ProcessExit += (sender, args) =>
- cts.Cancel();
-
- using (var act = _compositor5.RequestCommitAsync())
- act.SetCompleted(new RunLoopHandler(this));
-
- while (!cts.IsCancellationRequested)
- {
- UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0);
- lock (_pumpLock)
- UnmanagedMethods.DispatchMessage(ref msg);
- }
- }
-
- public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle,
- float? backdropCornerRadius)
- {
- const int majorRequired = 10;
- const int buildRequired = 17134;
-
- var majorInstalled = Win32Platform.WindowsVersion.Major;
- var buildInstalled = Win32Platform.WindowsVersion.Build;
-
- if (majorInstalled >= majorRequired &&
- buildInstalled >= buildRequired)
- {
- try
- {
- TryCreateAndRegisterCore(angle, backdropCornerRadius);
- return;
- }
- catch (Exception e)
- {
- Logger.TryGet(LogEventLevel.Error, "WinUIComposition")
- ?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
-
- }
- }
-
- 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}");
- }
-
-
- public WinUICompositedWindow CreateWindow(IntPtr hWnd)
- {
- using var sc = _syncContext.EnsureLocked();
- using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0);
- using var target = desktopTarget.QueryInterface();
- using var device2 = _device.QueryInterface();
-
- using var drawingSurface = device2.CreateDrawingSurface2(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized,
- DirectXAlphaMode.Premultiplied);
- using var surface = drawingSurface.QueryInterface();
- using var surfaceInterop = drawingSurface.QueryInterface();
-
- using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface);
- using var brush = surfaceBrush.QueryInterface();
-
- using var spriteVisual = _compositor.CreateSpriteVisual();
- spriteVisual.SetBrush(brush);
- using var visual = spriteVisual.QueryInterface();
- using var visual2 = spriteVisual.QueryInterface();
- using var container = _compositor.CreateContainerVisual();
- using var containerVisual = container.QueryInterface();
- using var containerVisual2 = container.QueryInterface();
- containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1));
- using var containerChildren = container.Children;
-
- target.SetRoot(containerVisual);
-
- using var blur = CreateBlurVisual(_blurBrush);
- IVisual mica = null;
- if (_micaBrush != null)
- {
- mica = CreateBlurVisual(_micaBrush);
- containerChildren.InsertAtTop(mica);
- }
-
- var compositionRoundedRectangleGeometry = ClipVisual(blur, mica);
-
- containerChildren.InsertAtTop(blur);
- containerChildren.InsertAtTop(visual);
-
- return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual,
- blur, mica, compositionRoundedRectangleGeometry);
- }
-
- private ICompositionBrush CreateMicaBackdropBrush()
- {
- if (Win32Platform.WindowsVersion.Build < 22000)
- return null;
-
- using var compositorWithBlurredWallpaperBackdropBrush =
- _compositor.QueryInterface();
- using var blurredWallpaperBackdropBrush =
- compositorWithBlurredWallpaperBackdropBrush?.TryCreateBlurredWallpaperBackdropBrush();
- using var micaBackdropBrush = blurredWallpaperBackdropBrush?.QueryInterface();
- return micaBackdropBrush.CloneReference();
- }
-
- private unsafe ICompositionBrush CreateAcrylicBlurBackdropBrush()
- {
- using var backDropParameterFactory = NativeWinRTMethods.CreateActivationFactory(
- "Windows.UI.Composition.CompositionEffectSourceParameter");
- using var backdropString = new HStringInterop("backdrop");
- using var backDropParameter =
- backDropParameterFactory.Create(backdropString.Handle);
- using var backDropParameterAsSource = backDropParameter.QueryInterface();
- var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource);
- using var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect);
- using var compositionEffectBrush = blurEffectFactory.CreateBrush();
- using var backdropBrush = CreateBackdropBrush();
-
- var saturateEffect = new SaturationEffect(blurEffect);
- using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect);
- using var sat = satEffectFactory.CreateBrush();
- compositionEffectBrush.SetSourceParameter(backdropString.Handle, backdropBrush);
- return compositionEffectBrush.QueryInterface();
- }
-
- private ICompositionRoundedRectangleGeometry ClipVisual(params IVisual[] containerVisuals)
- {
- if (!_backdropCornerRadius.HasValue)
- return null;
- using var roundedRectangleGeometry = _compositor5.CreateRoundedRectangleGeometry();
- roundedRectangleGeometry.SetCornerRadius(new Vector2(_backdropCornerRadius.Value, _backdropCornerRadius.Value));
-
- using var compositor6 = _compositor.QueryInterface();
- using var compositionGeometry = roundedRectangleGeometry
- .QueryInterface();
-
- using var geometricClipWithGeometry =
- compositor6.CreateGeometricClipWithGeometry(compositionGeometry);
- foreach (var visual in containerVisuals)
- {
- visual?.SetClip(geometricClipWithGeometry.QueryInterface());
- }
-
- return roundedRectangleGeometry.CloneReference();
- }
-
- private unsafe IVisual CreateBlurVisual(ICompositionBrush compositionBrush)
- {
- using var spriteVisual = _compositor.CreateSpriteVisual();
- using var visual = spriteVisual.QueryInterface();
- using var visual2 = spriteVisual.QueryInterface();
-
-
- spriteVisual.SetBrush(compositionBrush);
- visual.SetIsVisible(0);
- visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f));
-
- return visual.CloneReference();
- }
-
- private ICompositionBrush CreateBackdropBrush()
- {
- ICompositionBackdropBrush brush = null;
- try
- {
- if (Win32Platform.WindowsVersion >= MinHostBackdropVersion)
- {
- using var compositor3 = _compositor.QueryInterface();
- brush = compositor3.CreateHostBackdropBrush();
- }
- else
- {
- using var compositor2 = _compositor.QueryInterface();
- brush = compositor2.CreateBackdropBrush();
- }
-
- return brush.QueryInterface();
- }
- finally
- {
- brush?.Dispose();
- }
- }
-
- public event Action Tick;
- public bool RunsInBackground => true;
- }
-}
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs
new file mode 100644
index 0000000000..32019f4c15
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Numerics;
+using System.Reactive.Disposables;
+using System.Threading;
+using Avalonia.OpenGL.Egl;
+using Avalonia.Win32.Interop;
+using MicroCom.Runtime;
+
+namespace Avalonia.Win32.WinRT.Composition;
+
+internal class WinUiCompositedWindow : IDisposable
+{
+ public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; }
+ private readonly WinUiCompositionShared _shared;
+ private readonly ICompositionRoundedRectangleGeometry _compositionRoundedRectangleGeometry;
+ private readonly IVisual _mica;
+ private readonly IVisual _blur;
+ private readonly IVisual _visual;
+ private PixelSize _size;
+ private readonly ICompositionSurfaceBrush _surfaceBrush;
+ private readonly ICompositionTarget _target;
+
+ public void Dispose()
+ {
+ lock (_shared.SyncRoot)
+ {
+ _compositionRoundedRectangleGeometry?.Dispose();
+ _blur?.Dispose();
+ _mica?.Dispose();
+ _visual.Dispose();
+ _surfaceBrush.Dispose();
+ _target.Dispose();
+ }
+ }
+
+ public WinUiCompositedWindow(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info,
+ WinUiCompositionShared shared, float? backdropCornerRadius)
+ {
+ WindowInfo = info;
+ _shared = shared;
+ using var desktopTarget = shared.DesktopInterop.CreateDesktopWindowTarget(WindowInfo.Handle, 0);
+ _target = desktopTarget.QueryInterface();
+
+
+ using var container = shared.Compositor.CreateContainerVisual();
+ using var containerVisual = container.QueryInterface();
+ using var containerVisual2 = container.QueryInterface();
+ containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1));
+ using var containerChildren = container.Children;
+
+ _target.SetRoot(containerVisual);
+
+ _blur = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.BlurBrush);
+ if (shared.MicaBrush != null)
+ {
+ _mica = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrush);
+ containerChildren.InsertAtTop(_mica);
+ }
+
+ _compositionRoundedRectangleGeometry =
+ WinUiCompositionUtils.ClipVisual(shared.Compositor, backdropCornerRadius, _blur, _mica);
+
+ containerChildren.InsertAtTop(_blur);
+ using var spriteVisual = shared.Compositor.CreateSpriteVisual();
+ _visual = spriteVisual.QueryInterface();
+ containerChildren.InsertAtTop(_visual);
+
+ _surfaceBrush = shared.Compositor.CreateSurfaceBrush();
+ using var compositionBrush = _surfaceBrush.QueryInterface();
+ spriteVisual.SetBrush(compositionBrush);
+ _target.SetRoot(containerVisual);
+
+
+
+ }
+
+ public void SetSurface(ICompositionSurface surface) => _surfaceBrush.SetSurface(surface);
+
+ public void SetBlur(BlurEffect blurEffect)
+ {
+ lock (_shared.SyncRoot)
+ {
+
+ _blur.SetIsVisible(blurEffect == BlurEffect.Acrylic
+ || blurEffect == BlurEffect.Mica && _mica == null ?
+ 1 :
+ 0);
+ _mica?.SetIsVisible(blurEffect == BlurEffect.Mica ? 1 : 0);
+ }
+ }
+
+ public IDisposable BeginTransaction()
+ {
+ Monitor.Enter(_shared.SyncRoot);
+ return Disposable.Create(() => Monitor.Exit(_shared.SyncRoot));
+ }
+
+ public void ResizeIfNeeded(PixelSize size)
+ {
+ lock (_shared.SyncRoot)
+ {
+ if (_size != size)
+ {
+ _visual.SetSize(new Vector2(size.Width, size.Height));
+ _compositionRoundedRectangleGeometry?.SetSize(new Vector2(size.Width, size.Height));
+ _size = size;
+ }
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
index fc04dcda26..9ee024f9ad 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs
@@ -1,119 +1,203 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.MicroCom;
+using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
+using Avalonia.Platform;
using Avalonia.Utilities;
+using Avalonia.Win32.DirectX;
using Avalonia.Win32.Interop;
+using Avalonia.Win32.OpenGl.Angle;
using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT.Composition
{
- internal class WinUiCompositedWindowSurface : EglGlPlatformSurfaceBase, IBlurHost, IDisposable
+ internal class WinUiCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, IBlurHost
{
- private readonly WinUICompositorConnection _connection;
- private EglPlatformOpenGlInterface _egl;
- private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
- private IRef _window;
+ private readonly WinUiCompositionShared _shared;
+ private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
+ private WinUiCompositedWindow _window;
private BlurEffect _blurEffect;
- public WinUiCompositedWindowSurface(WinUICompositorConnection connection, IEglWindowGlPlatformSurfaceInfo info) : base()
+ public WinUiCompositedWindowSurface(WinUiCompositionShared shared, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info)
{
- _connection = connection;
- _egl = connection.Egl;
+ _shared = shared;
_info = info;
}
- public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice)
{
- using (_egl.PrimaryContext.EnsureCurrent())
- {
- if (_window?.Item == null)
- {
- _window = RefCountable.Create(_connection.CreateWindow(_info.Handle));
- _window.Item.SetBlur(_blurEffect);
- }
+ var cornerRadius = AvaloniaLocator.Current.GetService()
+ ?.CompositionBackdropCornerRadius;
+ _window ??= new WinUiCompositedWindow(_info, _shared, cornerRadius);
+ _window.SetBlur(_blurEffect);
- return new CompositionRenderTarget(_egl, _window, _info);
- }
+ return new WinUiCompositedWindowRenderTarget(context, _window, d3dDevice, _shared.Compositor);
+ }
+
+ public void Dispose()
+ {
+ _window?.Dispose();
+ _window = null;
+ }
+
+ public void SetBlur(BlurEffect enable)
+ {
+ _blurEffect = enable;
+ _window?.SetBlur(enable);
}
+ }
- class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase
+ internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget
+ {
+ private readonly IPlatformGraphicsContext _context;
+ private readonly WinUiCompositedWindow _window;
+ private readonly IUnknown _d3dDevice;
+ private readonly ICompositor _compositor;
+ private readonly ICompositorInterop _interop;
+ private readonly ICompositionGraphicsDevice _compositionDevice;
+ private readonly ICompositionGraphicsDevice2 _compositionDevice2;
+ private readonly ICompositionSurface _surface;
+ private PixelSize _size;
+ private bool _lost;
+ private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
+
+ public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context,
+ WinUiCompositedWindow window, IntPtr device,
+ ICompositor compositor)
{
- private readonly EglPlatformOpenGlInterface _egl;
- private readonly IRef _window;
- private readonly IEglWindowGlPlatformSurfaceInfo _info;
-
- public CompositionRenderTarget(EglPlatformOpenGlInterface egl,
- IRef window,
- IEglWindowGlPlatformSurfaceInfo info)
- : base(egl)
+ _context = context;
+ _window = window;
+
+ try
{
- _egl = egl;
- _window = window.Clone();
- _info = info;
- _window.Item.ResizeIfNeeded(_info.Size);
+ _d3dDevice = MicroComRuntime.CreateProxyFor(device, false).CloneReference();
+ _compositor = compositor.CloneReference();
+ _interop = compositor.QueryInterface();
+ _compositionDevice = _interop.CreateGraphicsDevice(_d3dDevice);
+ _compositionDevice2 = _compositionDevice.QueryInterface();
+ _drawingSurface = _compositionDevice2.CreateDrawingSurface2(new UnmanagedMethods.SIZE(),
+ DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
+ _surface = _drawingSurface.QueryInterface();
+ _surfaceInterop = _drawingSurface.QueryInterface();
}
+ finally
+ {
+ if (_surfaceInterop == null)
+ Dispose();
+ }
+ }
- public override IGlPlatformSurfaceRenderingSession BeginDraw()
+ public void Dispose()
+ {
+ _surface?.Dispose();
+ _surfaceInterop?.Dispose();
+ _drawingSurface?.Dispose();
+ _compositionDevice2?.Dispose();
+ _compositionDevice?.Dispose();
+ _interop?.Dispose();
+ _compositor?.Dispose();
+ _d3dDevice?.Dispose();
+ }
+
+ public bool IsCorrupted => _context.IsLost || _lost;
+ private static Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
+ private readonly ICompositionDrawingSurface _drawingSurface;
+
+ public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw()
+ {
+ if (IsCorrupted)
+ throw new RenderTargetCorruptedException();
+ var transaction = _window.BeginTransaction();
+ bool needsEndDraw = false;
+ try
{
- var contextLock = _egl.PrimaryEglContext.EnsureCurrent();
- IUnknown texture = null;
- EglSurface surface = null;
- IDisposable transaction = null;
- var success = false;
+ var size = _window.WindowInfo.Size;
+ var scale = _window.WindowInfo.Scaling;
+ _window.ResizeIfNeeded(size);
+ _window.SetSurface(_surface);
+
+ void* pTexture;
+ UnmanagedMethods.POINT off;
try
{
- if (_window?.Item == null)
- throw new ObjectDisposedException(GetType().FullName);
-
- var size = _info.Size;
- transaction = _window.Item.BeginTransaction();
- _window.Item.ResizeIfNeeded(size);
- texture = _window.Item.BeginDrawToTexture(out var offset);
-
- surface = ((AngleWin32EglDisplay) _egl.Display).WrapDirect3D11Texture(_egl,
- texture.GetNativeIntPtr(),
- offset.X, offset.Y, size.Width, size.Height);
-
- var res = base.BeginDraw(surface, _info, () =>
+ if (_size != size)
{
- surface?.Dispose();
- texture?.Dispose();
- _window.Item.EndDraw();
- transaction?.Dispose();
- contextLock?.Dispose();
- }, true);
- success = true;
- return res;
+ _surfaceInterop.Resize(new UnmanagedMethods.POINT
+ {
+ X = size.Width,
+ Y = size.Height
+ });
+ _size = size;
+ }
+ var iid = IID_ID3D11Texture2D;
+ off = _surfaceInterop.BeginDraw(null, &iid, &pTexture);
}
- finally
+ catch (Exception e)
{
- if (!success)
- {
- surface?.Dispose();
- texture?.Dispose();
- transaction?.Dispose();
- contextLock.Dispose();
- }
+ _lost = true;
+ throw new RenderTargetCorruptedException(e);
+ }
+
+ needsEndDraw = true;
+ var offset = new PixelPoint(off.X, off.Y);
+ using var texture = MicroComRuntime.CreateProxyFor(pTexture, true);
+
+ var session = new Session(_surfaceInterop, texture, transaction, _size, offset, scale);
+ transaction = null;
+ return session;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ if (needsEndDraw)
+ _surfaceInterop.EndDraw();
+ transaction?.Dispose();
}
}
}
- public void SetBlur(BlurEffect blurEffect)
+ class Session : IDirect3D11TextureRenderTargetRenderSession
{
- _blurEffect = blurEffect;
- _window?.Item?.SetBlur(blurEffect);
- }
+ private readonly IDisposable _transaction;
+ private readonly PixelSize _size;
+ private readonly PixelPoint _offset;
+ private readonly double _scaling;
+ private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
+ private readonly IUnknown _texture;
- public void Dispose()
- {
- using (_egl.PrimaryEglContext.EnsureLocked())
+ public Session(ICompositionDrawingSurfaceInterop surfaceInterop, IUnknown texture, IDisposable transaction,
+ PixelSize size, PixelPoint offset, double scaling)
{
- _window?.Dispose();
- _window = null;
+ _transaction = transaction;
+ _size = size;
+ _offset = offset;
+ _scaling = scaling;
+ _surfaceInterop = surfaceInterop.CloneReference();
+ _texture = texture.CloneReference();
}
+
+ public void Dispose()
+ {
+ try
+ {
+ _texture.Dispose();
+ _surfaceInterop.EndDraw();
+ _surfaceInterop.Dispose();
+ }
+ finally
+ {
+ _transaction.Dispose();
+ }
+ }
+
+ public IntPtr D3D11Texture2D => _texture.GetNativeIntPtr();
+ public PixelSize Size => _size;
+ public PixelPoint Offset => _offset;
+ public double Scaling => _scaling;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
new file mode 100644
index 0000000000..301e605e7a
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
@@ -0,0 +1,34 @@
+using System;
+using MicroCom.Runtime;
+
+namespace Avalonia.Win32.WinRT.Composition;
+
+internal class WinUiCompositionShared : IDisposable
+{
+ public ICompositor Compositor { get; }
+ public ICompositor5 Compositor5 { get; }
+ public ICompositorDesktopInterop DesktopInterop { get; }
+ public ICompositionBrush BlurBrush;
+ public ICompositionBrush MicaBrush;
+ public object SyncRoot { get; } = new();
+
+ public static readonly Version MinHostBackdropVersion = new Version(10, 0, 22000);
+
+ public WinUiCompositionShared(ICompositor compositor)
+ {
+ Compositor = compositor.CloneReference();
+ Compositor5 = compositor.QueryInterface();
+ BlurBrush = WinUiCompositionUtils.CreateAcrylicBlurBackdropBrush(compositor);
+ MicaBrush = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor);
+ DesktopInterop = compositor.QueryInterface();
+ }
+
+ public void Dispose()
+ {
+ BlurBrush.Dispose();
+ MicaBrush.Dispose();
+ DesktopInterop.Dispose();
+ Compositor.Dispose();
+ Compositor5.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs
new file mode 100644
index 0000000000..e7af4046d6
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Numerics;
+using MicroCom.Runtime;
+
+namespace Avalonia.Win32.WinRT.Composition;
+
+internal static class WinUiCompositionUtils
+{
+ public static ICompositionBrush CreateMicaBackdropBrush(ICompositor compositor)
+ {
+ if (Win32Platform.WindowsVersion.Build < 22000)
+ return null;
+
+ using var compositorWithBlurredWallpaperBackdropBrush =
+ compositor.QueryInterface();
+ using var blurredWallpaperBackdropBrush =
+ compositorWithBlurredWallpaperBackdropBrush?.TryCreateBlurredWallpaperBackdropBrush();
+ return blurredWallpaperBackdropBrush?.QueryInterface();
+ }
+
+ public static unsafe ICompositionBrush CreateAcrylicBlurBackdropBrush(ICompositor compositor)
+ {
+ using var backDropParameterFactory =
+ NativeWinRTMethods.CreateActivationFactory(
+ "Windows.UI.Composition.CompositionEffectSourceParameter");
+ using var backdropString = new HStringInterop("backdrop");
+ using var backDropParameter =
+ backDropParameterFactory.Create(backdropString.Handle);
+ using var backDropParameterAsSource = backDropParameter.QueryInterface();
+ var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource);
+ using var blurEffectFactory = compositor.CreateEffectFactory(blurEffect);
+ using var compositionEffectBrush = blurEffectFactory.CreateBrush();
+ using var backdropBrush = CreateBackdropBrush(compositor);
+
+ var saturateEffect = new SaturationEffect(blurEffect);
+ using var satEffectFactory = compositor.CreateEffectFactory(saturateEffect);
+ using var sat = satEffectFactory.CreateBrush();
+ compositionEffectBrush.SetSourceParameter(backdropString.Handle, backdropBrush);
+ return compositionEffectBrush.QueryInterface();
+ }
+
+ public static ICompositionRoundedRectangleGeometry ClipVisual(ICompositor compositor, float? _backdropCornerRadius, params IVisual[] containerVisuals)
+ {
+ if (!_backdropCornerRadius.HasValue)
+ return null;
+ using var compositor5 = compositor.QueryInterface();
+ using var roundedRectangleGeometry = compositor5.CreateRoundedRectangleGeometry();
+ roundedRectangleGeometry.SetCornerRadius(new Vector2(_backdropCornerRadius.Value, _backdropCornerRadius.Value));
+
+ using var compositor6 = compositor.QueryInterface();
+ using var compositionGeometry = roundedRectangleGeometry
+ .QueryInterface();
+
+ using var geometricClipWithGeometry =
+ compositor6.CreateGeometricClipWithGeometry(compositionGeometry);
+ foreach (var visual in containerVisuals)
+ {
+ visual?.SetClip(geometricClipWithGeometry.QueryInterface());
+ }
+
+ return roundedRectangleGeometry.CloneReference();
+ }
+
+ public static IVisual CreateBlurVisual(ICompositor compositor, ICompositionBrush compositionBrush)
+ {
+ using var spriteVisual = compositor.CreateSpriteVisual();
+ using var visual = spriteVisual.QueryInterface();
+ using var visual2 = spriteVisual.QueryInterface();
+
+
+ spriteVisual.SetBrush(compositionBrush);
+ visual.SetIsVisible(0);
+ visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f));
+
+ return visual.CloneReference();
+ }
+
+ public static ICompositionBrush CreateBackdropBrush(ICompositor compositor)
+ {
+ ICompositionBackdropBrush brush = null;
+ try
+ {
+ if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion)
+ {
+ using var compositor3 = compositor.QueryInterface();
+ brush = compositor3.CreateHostBackdropBrush();
+ }
+ else
+ {
+ using var compositor2 = compositor.QueryInterface();
+ brush = compositor2.CreateBackdropBrush();
+ }
+
+ return brush.QueryInterface();
+ }
+ finally
+ {
+ brush?.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
new file mode 100644
index 0000000000..a1408baae0
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositorConnection.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Logging;
+using Avalonia.MicroCom;
+using Avalonia.OpenGL.Egl;
+using Avalonia.Rendering;
+using Avalonia.Win32.DirectX;
+using Avalonia.Win32.Interop;
+using Avalonia.Win32.OpenGl.Angle;
+using MicroCom.Runtime;
+
+namespace Avalonia.Win32.WinRT.Composition;
+
+internal class WinUiCompositorConnection : IRenderTimer
+{
+ private readonly WinUiCompositionShared _shared;
+ public event Action Tick;
+ public bool RunsInBackground => true;
+
+ public unsafe WinUiCompositorConnection()
+ {
+ using var compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor");
+ /*
+ var levels = new[] { D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1 };
+ DirectXUnmanagedMethods.D3D11CreateDevice(IntPtr.Zero, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE,
+ IntPtr.Zero, 0, levels, (uint)levels.Length, 7, out var pD3dDevice, out var level, null);
+
+ var d3dDevice = MicroComRuntime.CreateProxyFor(pD3dDevice, true);
+
+ var compositionDevice = compositor.QueryInterface().CreateGraphicsDevice(d3dDevice);
+ var surf = compositionDevice.CreateDrawingSurface(new UnmanagedMethods.SIZE_F { X = 100, Y = 100 },
+ DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
+ var surfInterop = surf.QueryInterface();
+ var IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
+ void* texture = null;
+ surfInterop.BeginDraw(null, &IID_ID3D11Texture2D, &texture);
+ */
+
+ _shared = new WinUiCompositionShared(compositor);
+ }
+
+ static bool TryCreateAndRegisterCore()
+ {
+ var tcs = new TaskCompletionSource();
+ var pumpLock = new object();
+ var th = new Thread(() =>
+ {
+ WinUiCompositorConnection connect;
+ try
+ {
+ NativeWinRTMethods.CreateDispatcherQueueController(new NativeWinRTMethods.DispatcherQueueOptions
+ {
+ apartmentType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE,
+ dwSize = Marshal.SizeOf(),
+ threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT
+ });
+ connect = new WinUiCompositorConnection();
+ AvaloniaLocator.CurrentMutable.BindToSelf(connect);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect);
+ tcs.SetResult(true);
+
+ }
+ catch (Exception e)
+ {
+ tcs.SetException(e);
+ return;
+ }
+
+ connect.RunLoop();
+ })
+ {
+ IsBackground = true,
+ Name = "DwmRenderTimerLoop"
+ };
+ th.SetApartmentState(ApartmentState.STA);
+ th.Start();
+ return tcs.Task.Result;
+ }
+
+ class RunLoopHandler : CallbackBase, IAsyncActionCompletedHandler
+ {
+ private readonly WinUiCompositorConnection _parent;
+ private Stopwatch _st = Stopwatch.StartNew();
+
+ public RunLoopHandler(WinUiCompositorConnection parent)
+ {
+ _parent = parent;
+ }
+
+ public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus)
+ {
+ _parent.Tick?.Invoke(_st.Elapsed);
+ using var act = _parent._shared.Compositor5.RequestCommitAsync();
+ act.SetCompleted(this);
+ }
+ }
+
+ private void RunLoop()
+ {
+ var cts = new CancellationTokenSource();
+ AppDomain.CurrentDomain.ProcessExit += (sender, args) =>
+ cts.Cancel();
+
+ lock (_shared.SyncRoot)
+ using (var act = _shared.Compositor5.RequestCommitAsync())
+ act.SetCompleted(new RunLoopHandler(this));
+
+ while (!cts.IsCancellationRequested)
+ {
+ UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0);
+ lock (_shared.SyncRoot)
+ UnmanagedMethods.DispatchMessage(ref msg);
+ }
+ }
+
+ public static void TryCreateAndRegister()
+ {
+ const int majorRequired = 10;
+ const int buildRequired = 17134;
+
+ var majorInstalled = Win32Platform.WindowsVersion.Major;
+ var buildInstalled = Win32Platform.WindowsVersion.Build;
+
+ if (majorInstalled >= majorRequired &&
+ buildInstalled >= buildRequired)
+ {
+ try
+ {
+ TryCreateAndRegisterCore();
+ return;
+ }
+ catch (Exception e)
+ {
+ Logger.TryGet(LogEventLevel.Error, "WinUIComposition")
+ ?.Log(null, "Unable to initialize WinUI compositor: {0}", e);
+
+ }
+ }
+
+ 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}");
+ }
+
+ public WinUiCompositedWindowSurface CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new(_shared, info);
+}
diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs
index 786d698daa..35f0737d5f 100644
--- a/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs
@@ -3,7 +3,7 @@
namespace Avalonia.Win32.WinRT
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
- public struct WinRTColor
+ public record struct WinRTColor
{
public byte A;
public byte R;
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
index 5537a0b704..8c362b0c29 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
@@ -10,6 +10,7 @@ using Avalonia.Controls.Remote;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
+using Avalonia.Threading;
using Avalonia.Win32.Automation;
using Avalonia.Win32.Input;
using Avalonia.Win32.Interop.Automation;
@@ -69,7 +70,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_CLOSE:
{
- bool? preventClosing = Closing?.Invoke();
+ bool? preventClosing = Closing?.Invoke(WindowCloseReason.WindowClosing);
if (preventClosing == true)
{
return IntPtr.Zero;
@@ -106,6 +107,9 @@ namespace Avalonia.Win32
_touchDevice?.Dispose();
//Free other resources
Dispose();
+
+ // Schedule cleanup of anything that requires window to be destroyed
+ Dispatcher.UIThread.Post(AfterCloseCleanup);
return IntPtr.Zero;
}
@@ -134,13 +138,18 @@ namespace Avalonia.Win32
case WindowsMessage.WM_KEYDOWN:
case WindowsMessage.WM_SYSKEYDOWN:
{
- e = new RawKeyEventArgs(
- WindowsKeyboardDevice.Instance,
- timestamp,
- _owner,
- RawKeyEventType.KeyDown,
- KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)),
- WindowsKeyboardDevice.Instance.Modifiers);
+ var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam));
+
+ if (key != Key.None)
+ {
+ e = new RawKeyEventArgs(
+ WindowsKeyboardDevice.Instance,
+ timestamp,
+ _owner,
+ RawKeyEventType.KeyDown,
+ key,
+ WindowsKeyboardDevice.Instance.Modifiers);
+ }
break;
}
@@ -159,13 +168,18 @@ namespace Avalonia.Win32
case WindowsMessage.WM_KEYUP:
case WindowsMessage.WM_SYSKEYUP:
{
- e = new RawKeyEventArgs(
+ var key = KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam));
+
+ if (key != Key.None)
+ {
+ e = new RawKeyEventArgs(
WindowsKeyboardDevice.Instance,
timestamp,
_owner,
RawKeyEventType.KeyUp,
- KeyInterop.KeyFromVirtualKey(ToInt32(wParam), ToInt32(lParam)),
+ key,
WindowsKeyboardDevice.Instance.Modifiers);
+ }
break;
}
case WindowsMessage.WM_CHAR:
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 5f813eec1d..dbcc31c5f5 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -26,7 +26,8 @@ using static Avalonia.Win32.Interop.UnmanagedMethods;
using Avalonia.Collections.Pooled;
using Avalonia.Metadata;
using Avalonia.Platform.Storage;
-using Avalonia.Win32.DxgiSwapchain;
+using Avalonia.Win32.DirectX;
+using Avalonia.Win32.OpenGl.Angle;
namespace Avalonia.Win32
{
@@ -64,7 +65,6 @@ namespace Avalonia.Win32
private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1;
private bool _isUsingComposition;
- private bool _isUsingDxgiSwapchain;
private IBlurHost _blurHost;
private PlatformResizeReason _resizeReason;
private MOUSEMOVEPOINT _lastWmMousePoint;
@@ -79,7 +79,7 @@ namespace Avalonia.Win32
private readonly PenDevice _penDevice;
private readonly ManagedDeferredRendererLock _rendererLock;
private readonly FramebufferManager _framebuffer;
- private readonly IGlPlatformSurface _gl;
+ private readonly object _gl;
private readonly bool _wmPointerEnabled;
private Win32NativeControlHost _nativeControlHost;
@@ -136,23 +136,20 @@ namespace Avalonia.Win32
};
_rendererLock = new ManagedDeferredRendererLock();
- var glPlatform = AvaloniaLocator.Current.GetService();
+ var glPlatform = AvaloniaLocator.Current.GetService();
- var compositionConnector = AvaloniaLocator.Current.GetService();
+ var compositionConnector = AvaloniaLocator.Current.GetService();
- _isUsingComposition = compositionConnector is { } &&
- glPlatform is EglPlatformOpenGlInterface egl &&
- egl.Display is AngleWin32EglDisplay angleDisplay &&
- angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
+ var isUsingAngleDX11 = glPlatform is AngleWin32PlatformGraphics angle &&
+ angle.PlatformApi == AngleOptions.PlatformApi.DirectX11;
+ _isUsingComposition = compositionConnector is { } && isUsingAngleDX11;
DxgiConnection dxgiConnection = null;
+ var isUsingDxgiSwapchain = false;
if (!_isUsingComposition)
{
dxgiConnection = AvaloniaLocator.Current.GetService();
- _isUsingDxgiSwapchain = dxgiConnection is { } &&
- glPlatform is EglPlatformOpenGlInterface eglDxgi &&
- eglDxgi.Display is AngleWin32EglDisplay angleDisplayDxgi &&
- angleDisplayDxgi.PlatformApi == AngleOptions.PlatformApi.DirectX11;
+ isUsingDxgiSwapchain = dxgiConnection is { } && isUsingAngleDX11;
}
_wmPointerEnabled = Win32Platform.WindowsVersion >= PlatformConstants.Windows8;
@@ -164,24 +161,24 @@ namespace Avalonia.Win32
{
if (_isUsingComposition)
{
- var cgl = new WinUiCompositedWindowSurface(compositionConnector, this);
+ var cgl = compositionConnector.CreateSurface(this);
_blurHost = cgl;
_gl = cgl;
_isUsingComposition = true;
}
- else if (_isUsingDxgiSwapchain)
+ else if (isUsingDxgiSwapchain)
{
var dxgigl = new DxgiSwapchainWindow(dxgiConnection, this);
_gl = dxgigl;
}
else
{
- if (glPlatform is EglPlatformOpenGlInterface egl2)
- _gl = new EglGlPlatformSurface(egl2, this);
+ if (glPlatform is AngleWin32PlatformGraphics egl2)
+ _gl = new EglGlPlatformSurface(this);
else if (glPlatform is WglPlatformOpenGlInterface wgl)
- _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this);
+ _gl = new WglGlPlatformSurface(this);
}
}
@@ -194,7 +191,7 @@ namespace Avalonia.Win32
public Action Activated { get; set; }
- public Func Closing { get; set; }
+ public Func Closing { get; set; }
public Action Closed { get; set; }
@@ -434,7 +431,7 @@ namespace Avalonia.Win32
_ => BlurEffect.None
};
- if (Win32Platform.WindowsVersion >= WinUICompositorConnection.MinHostBackdropVersion)
+ if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion)
{
unsafe
{
@@ -443,7 +440,7 @@ namespace Avalonia.Win32
}
}
- if (Win32Platform.WindowsVersion < WinUICompositorConnection.MinHostBackdropVersion && effect == BlurEffect.Mica)
+ if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion && effect == BlurEffect.Mica)
{
effect = BlurEffect.Acrylic;
}
@@ -570,16 +567,22 @@ namespace Avalonia.Win32
return customRendererFactory.Create(root, loop);
if (Win32Platform.Compositor != null)
- return new CompositingRenderer(root, Win32Platform.Compositor);
-
+ return new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces);
+
return Win32Platform.UseDeferredRendering
? _isUsingComposition
- ? new DeferredRenderer(root, loop)
+ ? new DeferredRenderer(root, loop,
+ () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
+ Win32Platform.RenderInterface)
{
RenderOnlyOnRenderThread = true
}
- : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock)
- : new ImmediateRenderer((Visual)root);
+ : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock,
+ renderTargetFactory: () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
+ renderInterface: Win32Platform.RenderInterface)
+ : new ImmediateRenderer((Visual)root,
+ () => Win32Platform.RenderInterface.CreateRenderTarget(Surfaces),
+ Win32Platform.RenderInterface);
}
public void Resize(Size value, PlatformResizeReason reason)
@@ -640,12 +643,6 @@ namespace Avalonia.Win32
_hwnd = IntPtr.Zero;
}
- if (_className != null)
- {
- UnregisterClass(_className, GetModuleHandle(null));
- _className = null;
- }
-
_framebuffer.Dispose();
}
@@ -1032,6 +1029,12 @@ namespace Avalonia.Win32
{
var margins = UpdateExtendMargins();
DwmExtendFrameIntoClientArea(_hwnd, ref margins);
+
+ unsafe
+ {
+ int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_ROUND;
+ DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int));
+ }
}
else
{
@@ -1042,6 +1045,12 @@ namespace Avalonia.Win32
_extendedMargins = new Thickness();
Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout);
+
+ unsafe
+ {
+ int cornerPreference = (int)DwmWindowCornerPreference.DWMWCP_DEFAULT;
+ DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPreference, sizeof(int));
+ }
}
if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) &&
@@ -1141,6 +1150,15 @@ namespace Avalonia.Win32
}
}
+ private void AfterCloseCleanup()
+ {
+ if (_className != null)
+ {
+ UnregisterClass(_className, GetModuleHandle(null));
+ _className = null;
+ }
+ }
+
private void MaximizeWithoutCoveringTaskbar()
{
IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);
@@ -1340,8 +1358,6 @@ namespace Avalonia.Win32
}
private const int MF_BYCOMMAND = 0x0;
- private const int MF_BYPOSITION = 0x400;
- private const int MF_REMOVE = 0x1000;
private const int MF_ENABLED = 0x0;
private const int MF_GRAYED = 0x1;
private const int MF_DISABLED = 0x2;
diff --git a/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoProvider.cs b/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoProvider.cs
index e1b5f5a3a0..4f4e0b9293 100644
--- a/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoProvider.cs
+++ b/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoProvider.cs
@@ -8,7 +8,6 @@ namespace Avalonia.Win32
{
public IDisposable Listen(ObservableCollection mountedDrives)
{
- Contract.Requires(mountedDrives != null);
return new WindowsMountedVolumeInfoListener(mountedDrives);
}
}
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
index fb0857e472..a1836f3ce4 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaView.Text.cs
@@ -1,6 +1,5 @@
#nullable enable
using Avalonia.Input.TextInput;
-using JetBrains.Annotations;
using UIKit;
namespace Avalonia.iOS;
diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs
index 2080352020..48358c745f 100644
--- a/src/iOS/Avalonia.iOS/AvaloniaView.cs
+++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs
@@ -70,7 +70,8 @@ namespace Avalonia.iOS
// No-op
}
- public IRenderer CreateRenderer(IRenderRoot root) => new CompositingRenderer(root, Platform.Compositor);
+ public IRenderer CreateRenderer(IRenderRoot root) =>
+ new CompositingRenderer(root, Platform.Compositor, () => Surfaces);
public void Invalidate(Rect rect)
diff --git a/src/iOS/Avalonia.iOS/EaglDisplay.cs b/src/iOS/Avalonia.iOS/EaglDisplay.cs
index 906bbc29e7..7a5e1a496d 100644
--- a/src/iOS/Avalonia.iOS/EaglDisplay.cs
+++ b/src/iOS/Avalonia.iOS/EaglDisplay.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Reactive.Disposables;
using Avalonia.OpenGL;
using Avalonia.Platform;
@@ -6,29 +7,37 @@ using OpenGLES;
namespace Avalonia.iOS
{
- class EaglFeature : IPlatformOpenGlInterface
+ class EaglPlatformGraphics : IPlatformGraphics
{
- 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();
- public GlContext Context { get; } = new GlContext();
+ public IPlatformGraphicsContext GetSharedContext() => Context;
+
+ public bool UsesSharedContext => true;
+ public IPlatformGraphicsContext CreateContext() => throw new System.NotSupportedException();
+ public GlContext Context { get; }
+ public static GlVersion GlVersion { get; } = new(GlProfileType.OpenGLES, 3, 0);
+
+ public EaglPlatformGraphics()
+ {
+
+ const string path = "/System/Library/Frameworks/OpenGLES.framework/OpenGLES";
+ var libGl = ObjCRuntime.Dlfcn.dlopen(path, 1);
+ if (libGl == IntPtr.Zero)
+ throw new OpenGlException("Unable to load " + path);
+ var iface = new GlInterface(GlVersion, proc => ObjCRuntime.Dlfcn.dlsym(libGl, proc));
+ Context = new(iface, null);
+ }
}
class GlContext : IGlContext
{
public EAGLContext Context { get; private set; }
- public GlContext()
+ public GlContext(GlInterface glInterface, EAGLSharegroup sharegroup)
{
- const string path = "/System/Library/Frameworks/OpenGLES.framework/OpenGLES";
- var libGl = ObjCRuntime.Dlfcn.dlopen(path, 1);
- if (libGl == IntPtr.Zero)
- throw new OpenGlException("Unable to load " + path);
- GlInterface = new GlInterface(Version, proc => ObjCRuntime.Dlfcn.dlsym(libGl, proc));
- Context = new EAGLContext(EAGLRenderingAPI.OpenGLES3);
+ GlInterface = glInterface;
+ Context = sharegroup == null ?
+ new EAGLContext(EAGLRenderingAPI.OpenGLES3) :
+ new(EAGLRenderingAPI.OpenGLES3, sharegroup);
}
public void Dispose()
@@ -59,22 +68,34 @@ namespace Avalonia.iOS
public IDisposable MakeCurrent()
{
+ if (Context == null)
+ throw new PlatformGraphicsContextLostException();
var old = EAGLContext.CurrentContext;
if (!EAGLContext.SetCurrentContext(Context))
throw new OpenGlException("Unable to make context current");
return new ResetContext(old);
}
+ public bool IsLost => Context == null;
+
public IDisposable EnsureCurrent()
{
+ if (Context == null)
+ throw new PlatformGraphicsContextLostException();
if(EAGLContext.CurrentContext == Context)
return Disposable.Empty;
return MakeCurrent();
}
- public bool IsSharedWith(IGlContext context) => false;
+ public bool IsSharedWith(IGlContext context) => context is GlContext other
+ && ReferenceEquals(other.Context?.ShareGroup, Context?.ShareGroup);
+ public bool CanCreateSharedContext => true;
+ public IGlContext CreateSharedContext(IEnumerable preferredVersions = null)
+ {
+ return new GlContext(GlInterface, Context.ShareGroup);
+ }
- public GlVersion Version { get; } = new GlVersion(GlProfileType.OpenGLES, 3, 0);
+ public GlVersion Version => EaglPlatformGraphics.GlVersion;
public GlInterface GlInterface { get; }
public int SampleCount
{
@@ -92,5 +113,7 @@ namespace Avalonia.iOS
return stencil;
}
}
+
+ public object TryGetFeature(Type featureType) => null;
}
}
diff --git a/src/iOS/Avalonia.iOS/EaglLayerSurface.cs b/src/iOS/Avalonia.iOS/EaglLayerSurface.cs
index 0e8945d921..88812653d7 100644
--- a/src/iOS/Avalonia.iOS/EaglLayerSurface.cs
+++ b/src/iOS/Avalonia.iOS/EaglLayerSurface.cs
@@ -78,10 +78,12 @@ namespace Avalonia.iOS
throw new InvalidOperationException("Invalid thread, go away");
}
- public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
CheckThread();
var ctx = Platform.GlFeature.Context;
+ if (ctx != context)
+ throw new InvalidOperationException("Platform surface is only usable with tha main context");
using (ctx.MakeCurrent())
{
var fbo = new SizeSynchronizedLayerFbo(ctx.Context, ctx.GlInterface, _layer);
diff --git a/src/iOS/Avalonia.iOS/Platform.cs b/src/iOS/Avalonia.iOS/Platform.cs
index c2cf8c6f6c..eb0a55734a 100644
--- a/src/iOS/Avalonia.iOS/Platform.cs
+++ b/src/iOS/Avalonia.iOS/Platform.cs
@@ -25,18 +25,18 @@ namespace Avalonia.iOS
{
static class Platform
{
- public static EaglFeature GlFeature;
+ public static EaglPlatformGraphics GlFeature;
public static DisplayLinkTimer Timer;
internal static Compositor Compositor { get; private set; }
public static void Register()
{
- GlFeature ??= new EaglFeature();
+ GlFeature ??= new EaglPlatformGraphics();
Timer ??= new DisplayLinkTimer();
var keyboard = new KeyboardDevice();
AvaloniaLocator.CurrentMutable
- .Bind().ToConstant(GlFeature)
+ .Bind().ToConstant(GlFeature)
.Bind().ToConstant(new CursorFactoryStub())
.Bind().ToConstant(new WindowingPlatformStub())
.Bind().ToConstant(new ClipboardImpl())
@@ -50,7 +50,7 @@ namespace Avalonia.iOS
Compositor = new Compositor(
AvaloniaLocator.Current.GetRequiredService(),
- AvaloniaLocator.Current.GetService());
+ AvaloniaLocator.Current.GetService());
}
diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
index 602e5e4496..0162f53f5e 100644
--- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
+++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
@@ -17,6 +17,9 @@