Browse Source

Merge branch 'master' into fix-parent-service-provider

pull/9541/head
Max Katz 3 years ago
committed by GitHub
parent
commit
3b977c7b24
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      samples/ControlCatalog.NetCore/Program.cs
  2. 11
      samples/ControlCatalog.NetCore/Properties/launchSettings.json
  3. 10
      samples/ControlCatalog/ControlCatalog.csproj
  4. 29
      samples/ControlCatalog/Converter/DegToRadConverter.cs
  5. 3
      samples/ControlCatalog/MainView.xaml
  6. 107
      samples/ControlCatalog/Pages/CustomDrawing.xaml
  7. 67
      samples/ControlCatalog/Pages/CustomDrawing.xaml.cs
  8. 215
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  9. 2
      src/Avalonia.Base/Animation/CrossFade.cs
  10. 2
      src/Avalonia.Base/CornerRadius.cs
  11. 5
      src/Avalonia.Base/Media/ArcSegment.cs
  12. 6
      src/Avalonia.Base/Media/BezierSegment .cs
  13. 8
      src/Avalonia.Base/Media/BoxShadow.cs
  14. 2
      src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs
  15. 2
      src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs
  16. 2
      src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
  17. 6
      src/Avalonia.Base/Media/LineSegment.cs
  18. 2
      src/Avalonia.Base/Media/MatrixTransform.cs
  19. 2
      src/Avalonia.Base/Media/PathFigure.cs
  20. 2
      src/Avalonia.Base/Media/PathGeometry.cs
  21. 6
      src/Avalonia.Base/Media/QuadraticBezierSegment .cs
  22. 2
      src/Avalonia.Base/Media/RotateTransform.cs
  23. 2
      src/Avalonia.Base/Media/ScaleTransform.cs
  24. 2
      src/Avalonia.Base/Media/SkewTransform.cs
  25. 2
      src/Avalonia.Base/Media/Transform.cs
  26. 2
      src/Avalonia.Base/Media/TranslateTransform.cs
  27. 2
      src/Avalonia.Base/RelativePoint.cs
  28. 2
      src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
  29. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  30. 2
      src/Avalonia.Base/Rendering/DeferredRenderer.cs
  31. 2
      src/Avalonia.Base/Rendering/RendererBase.cs
  32. 2
      src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs
  33. 2
      src/Avalonia.Base/Styling/Activators/IStyleActivator.cs
  34. 2
      src/Avalonia.Base/Thickness.cs
  35. 1
      src/Avalonia.Base/Visual.cs
  36. 2
      src/Avalonia.Base/VisualTree/TransformedBounds.cs
  37. 2
      src/Avalonia.Build.Tasks/Extensions.cs
  38. 2
      src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
  39. 2
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  40. 2
      src/Avalonia.Controls/Selection/IndexRange.cs
  41. 2
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
  42. 2
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs
  43. 2
      src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs
  44. 16
      src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
  45. 18
      src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml
  46. 20
      src/Browser/Avalonia.Browser/Cursor.cs
  47. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  48. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
  49. 1
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  50. 139
      src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs
  51. 1370
      src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs
  52. 29
      src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs
  53. 202
      src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs
  54. 184
      src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs
  55. 32
      src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs
  56. 305
      src/Windows/Avalonia.Win32/DirectX/directx.idl
  57. 5
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  58. 10
      src/Windows/Avalonia.Win32/Win32Platform.cs
  59. 17
      src/Windows/Avalonia.Win32/WindowImpl.cs

9
samples/ControlCatalog.NetCore/Program.cs

@ -99,6 +99,15 @@ namespace ControlCatalog.NetCore
SilenceConsole();
return builder.StartLinuxDrm(args, scaling: GetScaling());
}
else if (args.Contains("--dxgi"))
{
builder.With(new Win32PlatformOptions()
{
UseLowLatencyDxgiSwapChain = true,
UseWindowsUIComposition = false
});
return builder.StartWithClassicDesktopLifetime(args);
}
else
return builder.StartWithClassicDesktopLifetime(args);
}

11
samples/ControlCatalog.NetCore/Properties/launchSettings.json

@ -0,0 +1,11 @@
{
"profiles": {
"ControlCatalog.NetCore": {
"commandName": "Project"
},
"Dxgi": {
"commandName": "Project",
"commandLineArgs": "--dxgi"
}
}
}

10
samples/ControlCatalog/ControlCatalog.csproj

@ -33,4 +33,14 @@
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" />
<ItemGroup>
<None Remove="Pages\CustomDrawing.xaml" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Update="Pages\CustomDrawing.xaml">
<Generator></Generator>
</AvaloniaResource>
</ItemGroup>
</Project>

29
samples/ControlCatalog/Converter/DegToRadConverter.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Avalonia.Data.Converters;
namespace ControlCatalog.Converter
{
public class DegToRadConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double rad)
{
return rad * 180.0d / Math.PI;
}
return 0.0d;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double deg)
{
return deg / 180.0d * Math.PI;
}
return 0.0d;
}
}
}

3
samples/ControlCatalog/MainView.xaml

@ -66,6 +66,9 @@
<TabItem Header="Cursor" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CursorPage />
</TabItem>
<TabItem Header="Custom Drawing" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CustomDrawing/>
</TabItem>
<TabItem Header="DataGrid"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">

107
samples/ControlCatalog/Pages/CustomDrawing.xaml

@ -0,0 +1,107 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:ControlCatalog.Pages"
xmlns:converters="using:ControlCatalog.Converter"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.CustomDrawing">
<UserControl.Resources>
<converters:DegToRadConverter x:Key="DegToRadConverter"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="Translation" HorizontalAlignment="Center"/>
<Grid ColumnDefinitions="*,*"
RowDefinitions="Auto,Auto"
>
<TextBlock Text="Horizontal"/>
<TextBlock Text="Vertical" Grid.Column="1"/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=ViewportCenterX,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=ViewportCenterY,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="1"
>
<TextBlock Text="Rotation" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
>
<Button Content="➖" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="RotateMinus"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=Rotation,
Converter={StaticResource DegToRadConverter},
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
<Button Content="➕" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="RotatePlus"
/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="2"
>
<TextBlock Text="Scale" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
>
<Button Content="➖" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="ZoomOut"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=Scale,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
<Button Content="➕" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="ZoomIn"
/>
</StackPanel>
</StackPanel>
<Grid Grid.Row="1" Grid.ColumnSpan="3" ClipToBounds="True">
<local:CustomDrawingExampleControl
x:Name="CustomDrawingControl"
/>
</Grid>
</Grid>
</UserControl>

67
samples/ControlCatalog/Pages/CustomDrawing.xaml.cs

@ -0,0 +1,67 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class CustomDrawing : UserControl
{
public CustomDrawing()
{
InitializeComponent();
}
private CustomDrawingExampleControl? _customControl;
public CustomDrawingExampleControl CustomDrawingControl
{
get
{
if (_customControl is not null)
return _customControl;
throw new System.Exception("Control did not get initialized");
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
var cntrl = this.FindControl<CustomDrawingExampleControl>("CustomDrawingControl");
if (cntrl != null)
{
_customControl = cntrl;
}
else
{
// be sad about it
}
}
private void RotateMinus (object? sender, RoutedEventArgs e)
{
if (_customControl is null) return;
_customControl.Rotation -= Math.PI / 20.0d;
}
private void RotatePlus(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Rotation += Math.PI / 20.0d;
}
private void ZoomIn(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Scale *= 1.2d;
}
private void ZoomOut(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Scale /= 1.2d;
}
}
}

215
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.Controls.Shapes;
namespace ControlCatalog.Pages
{
public class CustomDrawingExampleControl : Control
{
private Point _cursorPoint;
public StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
public StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
/// <summary>
/// Rotation, measured in Radians!
/// </summary>
public double Rotation
{
get => GetValue(RotationProperty);
set
{
double valueToUse = value % (Math.PI * 2);
SetValue(RotationProperty, valueToUse);
}
}
public StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
public double ViewportCenterY { get => GetValue(ViewportCenterYProperty); set => SetValue(ViewportCenterYProperty, value); }
public StyledProperty<double> ViewportCenterXProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterX), 0.0d);
public double ViewportCenterX { get => GetValue(ViewportCenterXProperty); set => SetValue(ViewportCenterXProperty, value); }
private IPen _pen;
private System.Diagnostics.Stopwatch _timeKeeper = System.Diagnostics.Stopwatch.StartNew();
private bool _isPointerCaptured = false;
public CustomDrawingExampleControl()
{
_pen = new Pen(new SolidColorBrush(Colors.Black), lineCap: PenLineCap.Round);
var _arc = new ArcSegment()
{
IsLargeArc = false,
Point = new Point(0, 0),
RotationAngle = 0,
Size = new Size(25, 25),
SweepDirection = SweepDirection.Clockwise,
};
StreamGeometry sg = new StreamGeometry();
var cntx = sg.Open();
cntx.BeginFigure(new Point(-25.0d, -10.0d), false);
cntx.ArcTo(new Point(25.0d, -10.0d), new Size(10.0d, 10.0d), 0.0d, false, SweepDirection.Clockwise);
cntx.EndFigure(true);
_smileGeometry = sg.Clone();
}
private Geometry _smileGeometry;
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
Point previousPoint = _cursorPoint;
_cursorPoint = e.GetPosition(this);
if (_isPointerCaptured)
{
Point oldWorldPoint = UIPointToWorldPoint(previousPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Vector diff = newWorldPoint - oldWorldPoint;
ViewportCenterX -= diff.X;
ViewportCenterY -= diff.Y;
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
e.Handled = true;
e.Pointer.Capture(this);
_isPointerCaptured = true;
base.OnPointerPressed(e);
}
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
base.OnPointerWheelChanged(e);
var oldScale = Scale;
Scale *= (1.0d + e.Delta.Y / 12.0d);
Point oldWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, oldScale, Rotation);
Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Vector diff = newWorldPoint - oldWorldPoint;
ViewportCenterX -= diff.X;
ViewportCenterY -= diff.Y;
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
e.Pointer.Capture(null);
_isPointerCaptured = false;
base.OnPointerReleased(e);
}
public override void Render(DrawingContext context)
{
var localBounds = new Rect(new Size(this.Bounds.Width, this.Bounds.Height));
var clip = context.PushClip(this.Bounds);
context.DrawRectangle(Brushes.White, _pen, localBounds, 1.0d);
var halfMax = Math.Max(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) * Math.Sqrt(2.0d);
var halfMin = Math.Min(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) / 1.3d;
var halfWidth = this.Bounds.Width / 2.0d;
var halfHeight = this.Bounds.Height / 2.0d;
// 0,0 refers to the top-left of the control now. It is not prime time to draw gui stuff because it'll be under the world
var translateModifier = context.PushPreTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
// now 0,0 refers to the ViewportCenter(X,Y).
var rotationMatrix = Avalonia.Matrix.CreateRotation(Rotation);
var rotationModifier = context.PushPreTransform(rotationMatrix);
// everything is rotated but not scaled
var scaleModifier = context.PushPreTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
var mapPositionModifier = context.PushPreTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
// now everything is rotated and scaled, and at the right position, now we're drawing strictly in world coordinates
context.DrawEllipse(Brushes.White, _pen, new Point(0.0d, 0.0d), 50.0d, 50.0d);
context.DrawLine(_pen, new Point(-25.0d, -5.0d), new Point(-25.0d, 15.0d));
context.DrawLine(_pen, new Point(25.0d, -5.0d), new Point(25.0d, 15.0d));
context.DrawGeometry(null, _pen, _smileGeometry);
Point cursorInWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
context.DrawEllipse(Brushes.Gray, _pen, cursorInWorldPoint, 20.0d, 20.0d);
for (int i = 0; i < 10; i++)
{
double orbitRadius = i * 100 + 200;
var orbitInput = ((_timeKeeper.Elapsed.TotalMilliseconds + 987654d) / orbitRadius) / 10.0d;
if (i % 3 == 0)
orbitInput *= -1;
Point orbitPosition = new Point(Math.Sin(orbitInput) * orbitRadius, Math.Cos(orbitInput) * orbitRadius);
context.DrawEllipse(Brushes.Gray, _pen, orbitPosition, 20.0d, 20.0d);
}
// end drawing the world
mapPositionModifier.Dispose();
scaleModifier.Dispose();
rotationModifier.Dispose();
translateModifier.Dispose();
// this is prime time to draw gui stuff
context.DrawLine(_pen, _cursorPoint + new Vector(-20, 0), _cursorPoint + new Vector(20, 0));
context.DrawLine(_pen, _cursorPoint + new Vector(0, -20), _cursorPoint + new Vector(0, 20));
clip.Dispose();
// oh and draw again when you can, no rush, right?
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
}
private Point UIPointToWorldPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
{
Point workingPoint = new Point(inPoint.X, -inPoint.Y);
workingPoint += new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
workingPoint /= scale;
workingPoint = Matrix.CreateRotation(rotation).Transform(workingPoint);
workingPoint += new Vector(viewportCenterX, viewportCenterY);
return workingPoint;
}
private Point WorldPointToUIPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
{
Point workingPoint = new Point(inPoint.X, inPoint.Y);
workingPoint -= new Vector(viewportCenterX, viewportCenterY);
// undo rotation
workingPoint = Matrix.CreateRotation(-rotation).Transform(workingPoint);
workingPoint *= scale;
workingPoint -= new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
workingPoint = new Point(workingPoint.X, -workingPoint.Y);
return workingPoint;
}
}
}

2
src/Avalonia.Base/Animation/CrossFade.cs

@ -10,7 +10,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Animation
{
/// <summary>
/// Defines a cross-fade animation between two <see cref="IVisual"/>s.
/// Defines a cross-fade animation between two <see cref="Visual"/>s.
/// </summary>
public class CrossFade : IPageTransition
{

2
src/Avalonia.Base/CornerRadius.cs

@ -100,7 +100,7 @@ namespace Avalonia
public override string ToString()
{
return $"{TopLeft},{TopRight},{BottomRight},{BottomLeft}";
return FormattableString.Invariant($"{TopLeft},{TopRight},{BottomRight},{BottomLeft}");
}
public static CornerRadius Parse(string s)

5
src/Avalonia.Base/Media/ArcSegment.cs

@ -1,3 +1,4 @@
using System;
using System.Globalization;
namespace Avalonia.Media
@ -100,6 +101,6 @@ namespace Avalonia.Media
}
public override string ToString()
=> $"A {Size} {RotationAngle.ToString(CultureInfo.InvariantCulture)} {(IsLargeArc ? 1 : 0)} {(int)SweepDirection} {Point}";
=> FormattableString.Invariant($"A {Size} {RotationAngle} {(IsLargeArc ? 1 : 0)} {(int)SweepDirection} {Point}");
}
}
}

6
src/Avalonia.Base/Media/BezierSegment .cs

@ -1,3 +1,5 @@
using System;
namespace Avalonia.Media
{
public sealed class BezierSegment : PathSegment
@ -60,6 +62,6 @@ namespace Avalonia.Media
}
public override string ToString()
=> $"C {Point1} {Point2} {Point3}";
=> FormattableString.Invariant($"C {Point1} {Point2} {Point3}");
}
}
}

8
src/Avalonia.Base/Media/BoxShadow.cs

@ -94,22 +94,22 @@ namespace Avalonia.Media
if (OffsetX != 0.0)
{
sb.AppendFormat(" {0}", OffsetX.ToString());
sb.AppendFormat(" {0}", OffsetX.ToString(CultureInfo.InvariantCulture));
}
if (OffsetY != 0.0)
{
sb.AppendFormat(" {0}", OffsetY.ToString());
sb.AppendFormat(" {0}", OffsetY.ToString(CultureInfo.InvariantCulture));
}
if (Blur != 0.0)
{
sb.AppendFormat(" {0}", Blur.ToString());
sb.AppendFormat(" {0}", Blur.ToString(CultureInfo.InvariantCulture));
}
if (Spread != 0.0)
{
sb.AppendFormat(" {0}", Spread.ToString());
sb.AppendFormat(" {0}", Spread.ToString(CultureInfo.InvariantCulture));
}
sb.AppendFormat(" {0}", Color.ToString());

2
src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs

@ -7,7 +7,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media.Imaging
{
/// <summary>
/// A bitmap that holds the rendering of a <see cref="IVisual"/>.
/// A bitmap that holds the rendering of a <see cref="Visual"/>.
/// </summary>
public class RenderTargetBitmap : Bitmap, IDisposable, IRenderTarget
{

2
src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs

@ -3,7 +3,7 @@
namespace Avalonia.Media.Immutable
{
/// <summary>
/// Represents a transform on an <see cref="IVisual"/>.
/// Represents a transform on an <see cref="Visual"/>.
/// </summary>
public class ImmutableTransform : ITransform
{

2
src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media.Immutable
{
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// Paints an area with an <see cref="Visual"/>.
/// </summary>
internal class ImmutableVisualBrush : ImmutableTileBrush, IVisualBrush
{

6
src/Avalonia.Base/Media/LineSegment.cs

@ -1,3 +1,5 @@
using System;
namespace Avalonia.Media
{
public sealed class LineSegment : PathSegment
@ -26,6 +28,6 @@ namespace Avalonia.Media
}
public override string ToString()
=> $"L {Point}";
=> FormattableString.Invariant($"L {Point}");
}
}
}

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

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Transforms an <see cref="IVisual"/> according to a <see cref="Matrix"/>.
/// Transforms an <see cref="Visual"/> according to a <see cref="Matrix"/>.
/// </summary>
public class MatrixTransform : Transform
{

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

@ -126,7 +126,7 @@ namespace Avalonia.Media
}
public override string ToString()
=> $"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty<PathSegment>())}{(IsClosed ? "Z" : "")}";
=> FormattableString.Invariant($"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty<PathSegment>())}{(IsClosed ? "Z" : "")}");
internal void ApplyTo(StreamGeometryContext ctx)
{

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

@ -133,7 +133,7 @@ namespace Avalonia.Media
public override string ToString()
{
var figuresString = _figures is not null ? string.Join(" ", _figures) : string.Empty;
return $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{figuresString}";
return FormattableString.Invariant($"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{figuresString}");
}
}
}

6
src/Avalonia.Base/Media/QuadraticBezierSegment .cs

@ -1,3 +1,5 @@
using System;
namespace Avalonia.Media
{
public sealed class QuadraticBezierSegment : PathSegment
@ -44,6 +46,6 @@ namespace Avalonia.Media
}
public override string ToString()
=> $"Q {Point1} {Point2}";
=> FormattableString.Invariant($"Q {Point1} {Point2}");
}
}
}

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

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Rotates an <see cref="IVisual"/>.
/// Rotates an <see cref="Visual"/>.
/// </summary>
public class RotateTransform : Transform
{

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

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Scale an <see cref="IVisual"/>.
/// Scale an <see cref="Visual"/>.
/// </summary>
public class ScaleTransform : Transform
{

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

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Skews an <see cref="IVisual"/>.
/// Skews an <see cref="Visual"/>.
/// </summary>
public class SkewTransform : Transform
{

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

@ -7,7 +7,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Represents a transform on an <see cref="IVisual"/>.
/// Represents a transform on an <see cref="Visual"/>.
/// </summary>
public abstract class Transform : Animatable, IMutableTransform
{

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

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Translates (moves) an <see cref="IVisual"/>.
/// Translates (moves) an <see cref="Visual"/>.
/// </summary>
public class TranslateTransform : Transform
{

2
src/Avalonia.Base/RelativePoint.cs

@ -199,7 +199,7 @@ namespace Avalonia
{
return _unit == RelativeUnit.Absolute ?
_point.ToString() :
string.Format(CultureInfo.InvariantCulture, "{0}%, {1}%", _point.X * 100, _point.Y * 100);
string.Format(CultureInfo.InvariantCulture, "{0}%, {1}%", _point.X * 100, _point.Y * 100);
}
}
}

2
src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs

@ -52,7 +52,7 @@ internal class FpsCounter
_lastFpsUpdate = now;
}
var fpsLine = $"Frame #{_totalFrames:00000000} FPS: {_fps:000} " + aux;
var fpsLine = FormattableString.Invariant($"Frame #{_totalFrames:00000000} FPS: {_fps:000} ") + aux;
double width = 0;
double height = 0;
foreach (var ch in fpsLine)

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -156,7 +156,7 @@ namespace Avalonia.Rendering.Composition.Server
(Compositor.BatchObjectPool.CurrentUsage + Compositor.BatchObjectPool.CurrentPool) *
Compositor.BatchObjectPool.ArraySize *
IntPtr.Size), false);
_fpsCounter.RenderFps(targetContext, $"M:{managedMem} / N:{nativeMem} R:{RenderedVisuals:0000}");
_fpsCounter.RenderFps(targetContext, FormattableString.Invariant($"M:{managedMem} / N:{nativeMem} R:{RenderedVisuals:0000}"));
}
RenderedVisuals = 0;

2
src/Avalonia.Base/Rendering/DeferredRenderer.cs

@ -725,7 +725,7 @@ namespace Avalonia.Rendering
foreach (var layer in Layers)
{
var fileName = Path.Combine(DebugFramesPath ?? string.Empty, $"frame-{id}-layer-{index++}.png");
var fileName = Path.Combine(DebugFramesPath ?? string.Empty, FormattableString.Invariant($"frame-{id}-layer-{index++}.png"));
layer.Bitmap.Item.Save(fileName);
}
}

2
src/Avalonia.Base/Rendering/RendererBase.cs

@ -36,7 +36,7 @@ namespace Avalonia.Rendering
_lastFpsUpdate = now;
}
var text = layerCount.HasValue ? $"Layers: {layerCount} FPS: {_fps:000}" : $"FPS: {_fps:000}";
var text = layerCount.HasValue ? FormattableString.Invariant($"Layers: {layerCount} FPS: {_fps:000}") : FormattableString.Invariant($"FPS: {_fps:000}");
var formattedText = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, s_fontSize, Brushes.White);

2
src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs

@ -10,7 +10,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the low-level scene graph representing an <see cref="IVisual"/>.
/// A node in the low-level scene graph representing an <see cref="Visual"/>.
/// </summary>
internal class VisualNode : IVisualNode
{

2
src/Avalonia.Base/Styling/Activators/IStyleActivator.cs

@ -39,7 +39,7 @@ namespace Avalonia.Styling.Activators
/// </summary>
/// <param name="sink">The listener.</param>
/// <remarks>
/// This method should not call <see cref="IStyleActivatorSink.OnNext(bool, int)"/>.
/// This method should not call <see cref="IStyleActivatorSink.OnNext(bool)"/>.
/// </remarks>
void Subscribe(IStyleActivatorSink sink);

2
src/Avalonia.Base/Thickness.cs

@ -277,7 +277,7 @@ namespace Avalonia
/// <returns>The string representation of the thickness.</returns>
public override string ToString()
{
return $"{_left},{_top},{_right},{_bottom}";
return FormattableString.Invariant($"{_left},{_top},{_right},{_bottom}");
}
/// <summary>

1
src/Avalonia.Base/Visual.cs

@ -417,6 +417,7 @@ namespace Avalonia
OnAttachedToVisualTree(e);
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
_visualRoot.Renderer?.RecalculateChildren(_visualParent!);
if (ZIndex != 0 && VisualParent is Visual parent)
parent.HasNonUniformZIndexChildren = true;

2
src/Avalonia.Base/VisualTree/TransformedBounds.cs

@ -77,6 +77,6 @@ namespace Avalonia.VisualTree
return !left.Equals(right);
}
public override string ToString() => $"Bounds: {Bounds} Clip: {Clip} Transform {Transform}";
public override string ToString() => FormattableString.Invariant($"Bounds: {Bounds} Clip: {Clip} Transform {Transform}");
}
}

2
src/Avalonia.Build.Tasks/Extensions.cs

@ -5,7 +5,7 @@ namespace Avalonia.Build.Tasks
{
static class Extensions
{
static string FormatErrorCode(BuildEngineErrorCode code) => $"AVLN:{(int)code:0000}";
static string FormatErrorCode(BuildEngineErrorCode code) => FormattableString.Invariant($"AVLN:{(int)code:0000}");
public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, Exception ex,
int lineNumber = 0, int linePosition = 0)

2
src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs

@ -67,7 +67,7 @@ namespace Avalonia.Build.Tasks
{
var src = new Source(r.ItemSpec, Root);
BuildEngine.LogMessage($"avares -> name:{src.Path}, path: {src.SystemPath}, size:{src.Size}, ItemSpec:{r.ItemSpec}", _reportImportance);
BuildEngine.LogMessage(FormattableString.Invariant($"avares -> name:{src.Path}, path: {src.SystemPath}, size:{src.Size}, ItemSpec:{r.ItemSpec}"), _reportImportance);
return src;
}).ToList();

2
src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs

@ -259,7 +259,7 @@ namespace Avalonia.Controls.Primitives
SelectedValue = (int)newSel * Increment + MinimumValue;
_suppressUpdateOffset = false;
System.Diagnostics.Debug.WriteLine($"Offset: {_offset} ItemHeight: {ItemHeight}");
System.Diagnostics.Debug.WriteLine(FormattableString.Invariant($"Offset: {_offset} ItemHeight: {ItemHeight}"));
}
}

2
src/Avalonia.Controls/Selection/IndexRange.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls.Selection
return hashCode;
}
public override string ToString() => $"[{Begin}..{End}]";
public override string ToString() => FormattableString.Invariant($"[{Begin}..{End}]");
public static bool operator ==(IndexRange left, IndexRange right) => left.Equals(right);
public static bool operator !=(IndexRange left, IndexRange right) => !(left == right);

2
src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs

@ -162,7 +162,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
if (sendNow != null && socket != null)
{
await socket.SendMessage(
$"frame:{sendNow.SequenceId}:{sendNow.Width}:{sendNow.Height}:{sendNow.Stride}:{sendNow.DpiX}:{sendNow.DpiY}");
FormattableString.Invariant($"frame:{sendNow.SequenceId}:{sendNow.Width}:{sendNow.Height}:{sendNow.Stride}:{sendNow.DpiX}:{sendNow.DpiY}"));
await socket.SendMessage(false, sendNow.Data);
}

2
src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs

@ -146,7 +146,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
public async Task RespondAsync(int code, byte[] data, string contentType)
{
var headers = Encoding.UTF8.GetBytes($"HTTP/1.1 {code} {(HttpStatusCode)code}\r\nConnection: close\r\nContent-Type: {contentType}\r\nContent-Length: {data.Length}\r\n\r\n");
var headers = Encoding.UTF8.GetBytes(FormattableString.Invariant($"HTTP/1.1 {code} {(HttpStatusCode)code}\r\nConnection: close\r\nContent-Type: {contentType}\r\nContent-Length: {data.Length}\r\n\r\n"));
await _stream.WriteAsync(headers, 0, headers.Length);
await _stream.WriteAsync(data, 0, data.Length);
_stream.Dispose();

2
src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs

@ -127,7 +127,7 @@ namespace Avalonia.FreeDesktop
var pid = Process.GetCurrentProcess().Id;
var tid = s_trayIconInstanceId++;
_sysTrayServiceName = $"org.kde.StatusNotifierItem-{pid}-{tid}";
_sysTrayServiceName = FormattableString.Invariant($"org.kde.StatusNotifierItem-{pid}-{tid}");
_statusNotifierItemDbusObj = new StatusNotifierItemDbusObj(_dbusMenuPath);
try

16
src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml

@ -35,13 +35,13 @@
<!-- To mimic WinUI SystemFocusVisual, Focus visual is drawn outside the bounds of the item -->
<Border Name="Root" Background="{TemplateBinding Background}"
BorderThickness="0" ClipToBounds="True">
<ContentControl Name="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
FontSize="{TemplateBinding FontSize}"
Margin="{TemplateBinding Padding}" />
<ContentPresenter Name="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
FontSize="{TemplateBinding FontSize}"
Margin="{TemplateBinding Padding}" />
</Border>
<!-- Drawn Border should render on top of background to preserve the 1px margin between items -->
@ -70,7 +70,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewSelectedBorderBrush}" />
</Style>
<Style Selector="^ /template/ ContentControl#Content">
<Style Selector="^ /template/ ContentPresenter#Content">
<Setter Property="Foreground" Value="{DynamicResource CalendarViewTodayForeground}" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>

18
src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml

@ -32,14 +32,14 @@
Opacity="0.5" />
<!-- Focusable="False" -->
<ContentControl Name="Content"
Margin="1,0,1,1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}" />
<ContentPresenter Name="Content"
Margin="1,0,1,1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}" />
<Rectangle Name="FocusVisual"
IsHitTestVisible="False"
@ -62,7 +62,7 @@
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="^:inactive /template/ ContentControl#Content">
<Style Selector="^:inactive /template/ ContentPresenter#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}" />
</Style>

20
src/Browser/Avalonia.Browser/Cursor.cs

@ -20,9 +20,9 @@ namespace Avalonia.Browser
/// </summary>
public CssCursor(string base64, string format, PixelPoint hotspot, StandardCursorType fallback)
{
Value = $"url(\"data:image/{format};base64,{base64}\") {hotspot.X} {hotspot.Y}, {ToKeyword(fallback)}";
Value = FormattableString.Invariant($"url(\"data:image/{format};base64,{base64}\") {hotspot.X} {hotspot.Y}, {ToKeyword(fallback)}");
}
/// <summary>
/// Create a cursor from url to *.cur file.
/// </summary>
@ -30,15 +30,15 @@ namespace Avalonia.Browser
{
Value = $"url('{url}'), {ToKeyword(fallback)}";
}
/// <summary>
/// Create a cursor from png/svg and hotspot position
/// </summary>
public CssCursor(string url, PixelPoint hotSpot, StandardCursorType fallback)
{
Value = $"url('{url}') {hotSpot.X} {hotSpot.Y}, {ToKeyword(fallback)}";
Value = FormattableString.Invariant($"url('{url}') {hotSpot.X} {hotSpot.Y}, {ToKeyword(fallback)}");
}
private static string ToKeyword(StandardCursorType type) => type switch
{
StandardCursorType.Hand => "pointer",
@ -49,16 +49,16 @@ namespace Avalonia.Browser
StandardCursorType.None => "none",
StandardCursorType.Wait => "progress",
StandardCursorType.AppStarting => "wait",
StandardCursorType.DragMove => "move",
StandardCursorType.DragCopy => "copy",
StandardCursorType.DragLink => "alias",
StandardCursorType.UpArrow => "default",/*not found matching one*/
StandardCursorType.SizeWestEast => "ew-resize",
StandardCursorType.SizeNorthSouth => "ns-resize",
StandardCursorType.SizeAll => "move",
StandardCursorType.TopSide => "n-resize",
StandardCursorType.BottomSide => "s-resize",
StandardCursorType.LeftSide => "w-resize",
@ -67,10 +67,10 @@ namespace Avalonia.Browser
StandardCursorType.TopRightCorner => "ne-resize",
StandardCursorType.BottomLeftCorner => "sw-resize",
StandardCursorType.BottomRightCorner => "se-resize",
_ => Default,
};
public void Dispose() {}
}

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -275,7 +275,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public int Level { get; }
public override string ToString()
=> $"$parent[{AncestorType?.Name},{Level}]";
=> FormattableString.Invariant($"$parent[{AncestorType?.Name},{Level}]");
}
internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
@ -316,7 +316,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public int[] Indices { get; }
public Type ElementType { get; }
public override string ToString()
=> $"[{string.Join(",", Indices)}]";
=> FormattableString.Invariant($"[{string.Join(",", Indices)}]");
}
internal class TypeCastPathElement<T> : ITypeCastElement

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs

@ -22,11 +22,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
if (_ancestorType == null)
{
return $"$visualparent[{_level}]";
return FormattableString.Invariant($"$visualparent[{_level}]");
}
else
{
return $"$visualparent[{_ancestorType.Name}, {_level}]";
return FormattableString.Invariant($"$visualparent[{_ancestorType.Name}, {_level}]");
}
}
}

1
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -14,6 +14,7 @@
<PackageReference Include="MicroCom.CodeGenerator.MSBuild" Version="0.11.0" PrivateAssets="all" />
<MicroComIdl Include="WinRT\winrt.idl" CSharpInteropPath="WinRT\WinRT.Generated.cs" />
<MicroComIdl Include="Win32Com\win32.idl" CSharpInteropPath="Win32Com\Win32.Generated.cs" />
<MicroComIdl Include="DirectX\directx.idl" CSharpInteropPath="DirectX\directx.Generated.cs" />
</ItemGroup>
<ItemGroup>
<Compile Remove="..\..\Shared\ModuleInitializer.cs" />

139
src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Win32.DxgiSwapchain
{
internal enum D3D_FEATURE_LEVEL
{
D3D_FEATURE_LEVEL_1_0_CORE = 0x1000,
D3D_FEATURE_LEVEL_9_1 = 0x9100,
D3D_FEATURE_LEVEL_9_2 = 0x9200,
D3D_FEATURE_LEVEL_9_3 = 0x9300,
D3D_FEATURE_LEVEL_10_0 = 0xa000,
D3D_FEATURE_LEVEL_10_1 = 0xa100,
D3D_FEATURE_LEVEL_11_0 = 0xb000,
D3D_FEATURE_LEVEL_11_1 = 0xb100,
D3D_FEATURE_LEVEL_12_0 = 0xc000,
D3D_FEATURE_LEVEL_12_1 = 0xc100,
D3D_FEATURE_LEVEL_12_2 = 0xc200,
}
internal enum D3D11_RESOURCE_DIMENSION
{
D3D11_USAGE_DEFAULT = 0,
D3D11_USAGE_IMMUTABLE = 1,
D3D11_USAGE_DYNAMIC = 2,
D3D11_USAGE_STAGING = 3,
}
internal enum D3D11_USAGE
{
D3D11_USAGE_DEFAULT = 0,
D3D11_USAGE_IMMUTABLE = 1,
D3D11_USAGE_DYNAMIC = 2,
D3D11_USAGE_STAGING = 3,
}
internal enum DXGI_SWAP_EFFECT
{
DXGI_SWAP_EFFECT_DISCARD = 0,
DXGI_SWAP_EFFECT_SEQUENTIAL = 1,
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL = 3,
DXGI_SWAP_EFFECT_FLIP_DISCARD = 4,
}
[Flags]
internal enum DXGI_SWAP_CHAIN_FLAG
{
DXGI_SWAP_CHAIN_FLAG_NONPREROTATED = 1,
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH = 2,
DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE = 4,
DXGI_SWAP_CHAIN_FLAG_RESTRICTED_CONTENT = 8,
DXGI_SWAP_CHAIN_FLAG_RESTRICT_SHARED_RESOURCE_DRIVER = 16,
DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY = 32,
DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT = 64,
DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER = 128,
DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO = 256,
DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO = 512,
DXGI_SWAP_CHAIN_FLAG_HW_PROTECTED = 1024,
DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING = 2048,
DXGI_SWAP_CHAIN_FLAG_RESTRICTED_TO_ALL_HOLOGRAPHIC_DISPLAYS = 4096,
}
internal enum DXGI_SCALING
{
DXGI_SCALING_STRETCH = 0,
DXGI_SCALING_NONE = 1,
DXGI_SCALING_ASPECT_RATIO_STRETCH = 2,
}
internal enum DXGI_RESIDENCY
{
DXGI_RESIDENCY_FULLY_RESIDENT = 1,
DXGI_RESIDENCY_RESIDENT_IN_SHARED_MEMORY = 2,
DXGI_RESIDENCY_EVICTED_TO_DISK = 3,
}
internal enum DXGI_MODE_ROTATION
{
DXGI_MODE_ROTATION_UNSPECIFIED = 0,
DXGI_MODE_ROTATION_IDENTITY = 1,
DXGI_MODE_ROTATION_ROTATE90 = 2,
DXGI_MODE_ROTATION_ROTATE180 = 3,
DXGI_MODE_ROTATION_ROTATE270 = 4,
}
internal enum DXGI_ALPHA_MODE
{
DXGI_ALPHA_MODE_UNSPECIFIED = 0,
DXGI_ALPHA_MODE_PREMULTIPLIED = 1,
DXGI_ALPHA_MODE_STRAIGHT = 2,
DXGI_ALPHA_MODE_IGNORE = 3,
DXGI_ALPHA_MODE_FORCE_DWORD = (unchecked((int)0xffffffff)),
}
}

1370
src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs

File diff suppressed because it is too large

29
src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Win32.DxgiSwapchain
{
internal unsafe class DirectXUnmanagedMethods
{
// these return HRESULTs expecting Marshall to throw Win32 exceptions on failures
[DllImport("dxgi", ExactSpelling = true, PreserveSig = false)]
internal static extern void CreateDXGIFactory(ref Guid riid, out void* ppFactory);
// these return HRESULTs expecting Marshall to throw Win32 exceptions on failures
[DllImport("dxgi", ExactSpelling = true, PreserveSig = false)]
internal static extern void CreateDXGIFactory1(ref Guid riid, out void* ppFactory);
[DllImport("user32", ExactSpelling = true)]
internal static extern bool GetMonitorInfoW(HANDLE hMonitor, IntPtr lpmi);
[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);
}
}

202
src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs

@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.DxgiSwapchain.DirectXUnmanagedMethods;
using MicroCom.Runtime;
namespace Avalonia.Win32.DxgiSwapchain
{
#pragma warning disable CA1416 // This should only be reachable on Windows
#nullable enable
public unsafe class DxgiConnection : IRenderTimer
{
public const uint ENUM_CURRENT_SETTINGS = unchecked((uint)(-1));
public bool RunsInBackground => true;
public event Action<TimeSpan>? Tick;
private AngleWin32EglDisplay _angle;
private EglPlatformOpenGlInterface _gl;
private object _syncLock;
private IDXGIOutput? _output = null;
private Stopwatch? _stopwatch = null;
public DxgiConnection(EglPlatformOpenGlInterface gl, object syncLock)
{
_syncLock = syncLock;
_angle = (AngleWin32EglDisplay)gl.Display;
_gl = gl;
}
public EglPlatformOpenGlInterface Egl => _gl;
public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle)
{
try
{
TryCreateAndRegisterCore(angle);
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
?.Log(null, "Unable to establish Dxgi: {0}", ex);
}
}
private unsafe void RunLoop()
{
_stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
GetBestOutputToVWaitOn();
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
}
while (true)
{
try
{
lock (_syncLock)
{
if (_output is not null)
{
try
{
_output.WaitForVBlank();
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
_output.Dispose();
_output = null;
GetBestOutputToVWaitOn();
}
}
else
{
// well since that obviously didn't work, then let's use the lowest-common-denominator instead
// for reference, this has never happened on my machine,
// but theoretically someone could have a weirder setup out there
DwmFlush();
}
Tick?.Invoke(_stopwatch.Elapsed);
}
}
catch (Exception ex)
{
Logger.TryGet(LogEventLevel.Error, nameof(DxgiSwapchain))
?.Log(this, $"Failed to wait for vblank, Exception: {ex.Message}, HRESULT = {ex.HResult}");
}
}
}
// Note: Defining best as display with highest refresh rate on
private void GetBestOutputToVWaitOn()
{
double highestRefreshRate = 0.0d;
// IDXGIFactory Guid: [Guid("7B7166EC-21C7-44AE-B21A-C9AE321AE369")]
Guid factoryGuid = MicroComRuntime.GetGuidFor(typeof(IDXGIFactory));
CreateDXGIFactory(ref factoryGuid, out var factPointer);
using var fact = MicroComRuntime.CreateProxyFor<IDXGIFactory>(factPointer, true);
void* adapterPointer = null;
ushort adapterIndex = 0;
// this looks odd, but that's just how one enumerates adapters in DXGI
while (fact.EnumAdapters(adapterIndex, &adapterPointer) == 0)
{
using var adapter = MicroComRuntime.CreateProxyFor<IDXGIAdapter>(adapterPointer, true);
void* outputPointer = null;
ushort outputIndex = 0;
while (adapter.EnumOutputs(outputIndex, &outputPointer) == 0)
{
using var output = MicroComRuntime.CreateProxyFor<IDXGIOutput>(outputPointer, true);
DXGI_OUTPUT_DESC outputDesc = output.Desc;
// this handle need not closing, by the way.
HANDLE monitorH = outputDesc.Monitor;
MONITORINFOEXW monInfo = default;
// by setting cbSize we tell Windows to fully populate the extended info
monInfo.Base.cbSize = sizeof(MONITORINFOEXW);
GetMonitorInfoW(monitorH, (IntPtr)(&monInfo));
DEVMODEW devMode = default;
EnumDisplaySettingsW(outputDesc.DeviceName, ENUM_CURRENT_SETTINGS, &devMode);
if (highestRefreshRate < devMode.dmDisplayFrequency)
{
// ooh I like this output!
if (_output is not null)
{
_output.Dispose();
_output = null;
}
_output = MicroComRuntime.CloneReference(output);
highestRefreshRate = devMode.dmDisplayFrequency;
}
// and then increment index to move onto the next monitor
outputIndex++;
}
// and then increment index to move onto the next display adapater
adapterIndex++;
}
}
// Used the windows composition as a blueprint for this startup/creation
static private bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface gl)
{
var tcs = new TaskCompletionSource<bool>();
var pumpLock = new object();
var thread = new System.Threading.Thread(() =>
{
try
{
DxgiConnection connection;
connection = new DxgiConnection(gl, pumpLock);
AvaloniaLocator.CurrentMutable.BindToSelf(connection);
AvaloniaLocator.CurrentMutable.Bind<IRenderTimer>().ToConstant(connection);
tcs.SetResult(true);
connection.RunLoop();
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
thread.IsBackground = true;
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
// block until
return tcs.Task.Result;
}
}
#nullable restore
#pragma warning restore CA1416 // Validate platform compatibility
}

184
src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using MicroCom.Runtime;
using static Avalonia.OpenGL.Egl.EglGlPlatformSurfaceBase;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.DxgiSwapchain
{
#pragma warning disable CA1416 // Validate platform compatibility, if you enter this not on windows you have messed up badly
#nullable enable
public unsafe class DxgiRenderTarget : EglPlatformSurfaceRenderTargetBase
{
// DXGI_FORMAT_B8G8R8A8_UNORM is target texture format as per ANGLE documentation
public const uint DXGI_USAGE_RENDER_TARGET_OUTPUT = 0x00000020U;
private IEglWindowGlPlatformSurfaceInfo _window;
private EglPlatformOpenGlInterface _egl;
private DxgiConnection _connection;
private IDXGIDevice? _dxgiDevice = null;
private IDXGIFactory2? _dxgiFactory = null;
private IDXGISwapChain1? _swapChain = null;
private IUnknown? _renderTexture = null;
private Interop.UnmanagedMethods.RECT _clientRect = default;
private uint _flagsUsed;
private Guid ID3D11Texture2DGuid = Guid.Parse("6F15AAF2-D208-4E89-9AB4-489535D34F9C");
public DxgiRenderTarget(IEglWindowGlPlatformSurfaceInfo window, EglPlatformOpenGlInterface egl, DxgiConnection connection) : base(egl)
{
_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<IUnknown>(((AngleWin32EglDisplay)_egl.Display).GetDirect3DDevice(), false);
_dxgiDevice = pdevice.QueryInterface<IDXGIDevice>();
// only needing the adapter pointer to ask it for the IDXGI Factory
using (var adapterPointer = _dxgiDevice.Adapter)
{
Guid factoryGuid = MicroComRuntime.GetGuidFor(typeof(IDXGIFactory2));
_dxgiFactory = MicroComRuntime.CreateProxyFor<IDXGIFactory2>(adapterPointer.GetParent(&factoryGuid), true);
}
DXGI_SWAP_CHAIN_DESC1 dxgiSwapChainDesc = new DXGI_SWAP_CHAIN_DESC1();
// standard swap chain really.
dxgiSwapChainDesc.Format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
dxgiSwapChainDesc.SampleDesc.Count = 1U;
dxgiSwapChainDesc.SampleDesc.Quality = 0U;
dxgiSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
dxgiSwapChainDesc.AlphaMode = DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_IGNORE;
dxgiSwapChainDesc.Width = (uint)_window.Size.Width;
dxgiSwapChainDesc.Height = (uint)_window.Size.Height;
dxgiSwapChainDesc.BufferCount = 2U;
dxgiSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_FLIP_DISCARD;
// okay I know this looks bad, but we're hitting our render-calls by awaiting via dxgi
// this is done in the DxgiConnection itself
_flagsUsed = dxgiSwapChainDesc.Flags = (uint)(DXGI_SWAP_CHAIN_FLAG.DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING);
_swapChain = _dxgiFactory.CreateSwapChainForHwnd
(
_dxgiDevice,
window.Handle,
&dxgiSwapChainDesc,
null,
null
);
Interop.UnmanagedMethods.RECT pClientRect;
GetClientRect(_window.Handle, out pClientRect);
_clientRect = pClientRect;
}
public override IGlPlatformSurfaceRenderingSession BeginDraw()
{
if (_swapChain is null)
{
throw new InvalidOperationException("No chain to draw on");
}
var contextLock = _egl.PrimaryContext.EnsureCurrent();
EglSurface? surface = null;
IDisposable? transaction = null;
var success = false;
try
{
Interop.UnmanagedMethods.RECT pClientRect;
GetClientRect(_window.Handle, out pClientRect);
if (!RectsEqual(pClientRect, _clientRect))
{
// we gotta resize
_clientRect = pClientRect;
if (_renderTexture is not null)
{
_renderTexture.Dispose();
_renderTexture = null;
}
_swapChain.ResizeBuffers(2,
(ushort)(pClientRect.right - pClientRect.left),
(ushort)(pClientRect.bottom - pClientRect.top),
DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
(ushort)_flagsUsed
);
}
var size = _window.Size;
// Get swapchain texture here
var texture = _renderTexture;
if (texture is null)
{
Guid textureGuid = ID3D11Texture2DGuid;
texture = MicroComRuntime.CreateProxyFor<IUnknown>(_swapChain.GetBuffer(0, &textureGuid), true);
}
_renderTexture = texture;
// I also have to get the pointer to this texture directly
surface = ((AngleWin32EglDisplay)_egl.Display).WrapDirect3D11Texture(_egl, MicroComRuntime.GetNativeIntPtr(_renderTexture),
0, 0, size.Width, size.Height);
var res = base.BeginDraw(surface, _window, () =>
{
_swapChain.Present((ushort)0U, (ushort)0U);
surface?.Dispose();
transaction?.Dispose();
contextLock?.Dispose();
}, true);
success = true;
return res;
}
finally
{
if (!success)
{
surface?.Dispose();
if (_renderTexture is not null)
{
_renderTexture.Dispose();
_renderTexture = null;
}
transaction?.Dispose();
contextLock.Dispose();
}
}
}
public override void Dispose()
{
base.Dispose();
_dxgiDevice?.Dispose();
_dxgiFactory?.Dispose();
_swapChain?.Dispose();
_renderTexture?.Dispose();
}
internal static bool RectsEqual(in RECT l, in RECT r)
{
return (l.left == r.left)
&& (l.top == r.top)
&& (l.right == r.right)
&& (l.bottom == r.bottom);
}
}
#pragma warning restore CA1416 // Validate platform compatibility
#nullable restore
}

32
src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.Win32.DxgiSwapchain
{
public class DxgiSwapchainWindow : EglGlPlatformSurfaceBase
{
private DxgiConnection _connection;
private EglPlatformOpenGlInterface _egl;
private IEglWindowGlPlatformSurfaceInfo _window;
public DxgiSwapchainWindow(DxgiConnection connection, IEglWindowGlPlatformSurfaceInfo window)
{
_connection = connection;
_window = window;
_egl = connection.Egl;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
using (_egl.PrimaryContext.EnsureCurrent())
{
return new DxgiRenderTarget(_window, _egl, _connection);
}
}
}
}

305
src/Windows/Avalonia.Win32/DirectX/directx.idl

@ -0,0 +1,305 @@
@clr-namespace Avalonia.Win32.DxgiSwapchain
@clr-access internal
@clr-map FLOAT float
@clr-map HSTRING IntPtr
@clr-map Vector2 System.Numerics.Vector2
@clr-map Vector3 System.Numerics.Vector3
@clr-map Quaternion System.Numerics.Quaternion
@clr-map Matrix4x4 System.Numerics.Matrix4x4
@clr-map RECT Avalonia.Win32.Interop.UnmanagedMethods.RECT
@clr-map SIZE Avalonia.Win32.Interop.UnmanagedMethods.SIZE
@clr-map POINT Avalonia.Win32.Interop.UnmanagedMethods.POINT
@clr-map HWND IntPtr
@clr-map BOOL int
@clr-map DWORD int
@clr-map boolean int
@clr-map BYTE byte
@clr-map INT16 short
@clr-map INT32 int
@clr-map INT64 long
@clr-map UINT ushort
@clr-map UINT16 ushort
@clr-map ULONG uint
@clr-map UINT32 uint
@clr-map UINT64 ulong
@clr-map DOUBLE double
@clr-map GUID System.Guid
@clr-map REFGUID System.Guid*
@clr-map REFIID System.Guid*
@clr-map WCHAR System.Char
@clr-map HMODULE void*
@clr-map DXGI_USAGE uint
@clr-map LARGE_INTEGER ulong
@clr-map INT int
@clr-map DXGI_SHARED_RESOURCE void*
@clr-map LUID ulong
@clr-map LPSTR ushort*
enum DXGI_FORMAT
{
DXGI_FORMAT_UNKNOWN = 0,
DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
DXGI_FORMAT_R32G32B32A32_UINT = 3,
DXGI_FORMAT_R32G32B32A32_SINT = 4,
DXGI_FORMAT_R32G32B32_TYPELESS = 5,
DXGI_FORMAT_R32G32B32_FLOAT = 6,
DXGI_FORMAT_R32G32B32_UINT = 7,
DXGI_FORMAT_R32G32B32_SINT = 8,
DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
DXGI_FORMAT_R16G16B16A16_UNORM = 11,
DXGI_FORMAT_R16G16B16A16_UINT = 12,
DXGI_FORMAT_R16G16B16A16_SNORM = 13,
DXGI_FORMAT_R16G16B16A16_SINT = 14,
DXGI_FORMAT_R32G32_TYPELESS = 15,
DXGI_FORMAT_R32G32_FLOAT = 16,
DXGI_FORMAT_R32G32_UINT = 17,
DXGI_FORMAT_R32G32_SINT = 18,
DXGI_FORMAT_R32G8X24_TYPELESS = 19,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
DXGI_FORMAT_R10G10B10A2_UNORM = 24,
DXGI_FORMAT_R10G10B10A2_UINT = 25,
DXGI_FORMAT_R11G11B10_FLOAT = 26,
DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
DXGI_FORMAT_R8G8B8A8_UNORM = 28,
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
DXGI_FORMAT_R8G8B8A8_UINT = 30,
DXGI_FORMAT_R8G8B8A8_SNORM = 31,
DXGI_FORMAT_R8G8B8A8_SINT = 32,
DXGI_FORMAT_R16G16_TYPELESS = 33,
DXGI_FORMAT_R16G16_FLOAT = 34,
DXGI_FORMAT_R16G16_UNORM = 35,
DXGI_FORMAT_R16G16_UINT = 36,
DXGI_FORMAT_R16G16_SNORM = 37,
DXGI_FORMAT_R16G16_SINT = 38,
DXGI_FORMAT_R32_TYPELESS = 39,
DXGI_FORMAT_D32_FLOAT = 40,
DXGI_FORMAT_R32_FLOAT = 41,
DXGI_FORMAT_R32_UINT = 42,
DXGI_FORMAT_R32_SINT = 43,
DXGI_FORMAT_R24G8_TYPELESS = 44,
DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
DXGI_FORMAT_R8G8_TYPELESS = 48,
DXGI_FORMAT_R8G8_UNORM = 49,
DXGI_FORMAT_R8G8_UINT = 50,
DXGI_FORMAT_R8G8_SNORM = 51,
DXGI_FORMAT_R8G8_SINT = 52,
DXGI_FORMAT_R16_TYPELESS = 53,
DXGI_FORMAT_R16_FLOAT = 54,
DXGI_FORMAT_D16_UNORM = 55,
DXGI_FORMAT_R16_UNORM = 56,
DXGI_FORMAT_R16_UINT = 57,
DXGI_FORMAT_R16_SNORM = 58,
DXGI_FORMAT_R16_SINT = 59,
DXGI_FORMAT_R8_TYPELESS = 60,
DXGI_FORMAT_R8_UNORM = 61,
DXGI_FORMAT_R8_UINT = 62,
DXGI_FORMAT_R8_SNORM = 63,
DXGI_FORMAT_R8_SINT = 64,
DXGI_FORMAT_A8_UNORM = 65,
DXGI_FORMAT_R1_UNORM = 66,
DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
DXGI_FORMAT_BC1_TYPELESS = 70,
DXGI_FORMAT_BC1_UNORM = 71,
DXGI_FORMAT_BC1_UNORM_SRGB = 72,
DXGI_FORMAT_BC2_TYPELESS = 73,
DXGI_FORMAT_BC2_UNORM = 74,
DXGI_FORMAT_BC2_UNORM_SRGB = 75,
DXGI_FORMAT_BC3_TYPELESS = 76,
DXGI_FORMAT_BC3_UNORM = 77,
DXGI_FORMAT_BC3_UNORM_SRGB = 78,
DXGI_FORMAT_BC4_TYPELESS = 79,
DXGI_FORMAT_BC4_UNORM = 80,
DXGI_FORMAT_BC4_SNORM = 81,
DXGI_FORMAT_BC5_TYPELESS = 82,
DXGI_FORMAT_BC5_UNORM = 83,
DXGI_FORMAT_BC5_SNORM = 84,
DXGI_FORMAT_B5G6R5_UNORM = 85,
DXGI_FORMAT_B5G5R5A1_UNORM = 86,
DXGI_FORMAT_B8G8R8A8_UNORM = 87,
DXGI_FORMAT_B8G8R8X8_UNORM = 88,
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
DXGI_FORMAT_BC6H_TYPELESS = 94,
DXGI_FORMAT_BC6H_UF16 = 95,
DXGI_FORMAT_BC6H_SF16 = 96,
DXGI_FORMAT_BC7_TYPELESS = 97,
DXGI_FORMAT_BC7_UNORM = 98,
DXGI_FORMAT_BC7_UNORM_SRGB = 99,
DXGI_FORMAT_AYUV = 100,
DXGI_FORMAT_Y410 = 101,
DXGI_FORMAT_Y416 = 102,
DXGI_FORMAT_NV12 = 103,
DXGI_FORMAT_P010 = 104,
DXGI_FORMAT_P016 = 105,
DXGI_FORMAT_420_OPAQUE = 106,
DXGI_FORMAT_YUY2 = 107,
DXGI_FORMAT_Y210 = 108,
DXGI_FORMAT_Y216 = 109,
DXGI_FORMAT_NV11 = 110,
DXGI_FORMAT_AI44 = 111,
DXGI_FORMAT_IA44 = 112,
DXGI_FORMAT_P8 = 113,
DXGI_FORMAT_A8P8 = 114,
DXGI_FORMAT_B4G4R4A4_UNORM = 115,
DXGI_FORMAT_P208 = 130,
DXGI_FORMAT_V208 = 131,
DXGI_FORMAT_V408 = 132,
DXGI_FORMAT_FORCE_UINT = -1
}
enum DXGI_MODE_SCANLINE_ORDER
{
DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED = 0,
DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE = 1,
DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST = 2,
DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST = 3
}
enum DXGI_MODE_SCALING
{
DXGI_MODE_SCALING_UNSPECIFIED = 0,
DXGI_MODE_SCALING_CENTERED = 1,
DXGI_MODE_SCALING_STRETCHED = 2
}
[uuid(aec22fb8-76f3-4639-9be0-28eb43a67a2e)]
interface IDXGIObject : IUnknown
{
HRESULT SetPrivateData([in] REFGUID Name, [in] UINT DataSize, [in] void** pData);
HRESULT SetPrivateDataInterface([in] REFGUID Name, [in] IUnknown* pUnknown);
HRESULT GetPrivateData([in] REFGUID Name, [in, out] UINT* pDataSize, [out] void** pData);
HRESULT GetParent([in] REFIID riid, [out, retval] void** ppParent);
}
[uuid(7b7166ec-21c7-44ae-b21a-c9ae321ae369)]
interface IDXGIFactory : IDXGIObject
{
INT32 EnumAdapters([in] UINT Adapter, [out] void* ppAdapter);
HRESULT MakeWindowAssociation(HWND WindowHandle, UINT Flags);
HRESULT GetWindowAssociation([out, annotation("_Out_")] HWND* pWindowHandle);
HRESULT CreateSwapChain([in, annotation("_In_")] IUnknown* pDevice, [in, annotation("_In_")] DXGI_SWAP_CHAIN_DESC* pDesc, [out, annotation("_COM_Outptr_")] IDXGISwapChain** ppSwapChain);
HRESULT CreateSoftwareAdapter([in] HMODULE Module, [out, annotation("_COM_Outptr_")] IDXGIAdapter** ppAdapter);
}
[uuid(3d3e0379-f9de-4d58-bb6c-18d62992f1a6)]
interface IDXGIDeviceSubObject : IDXGIObject
{
HRESULT GetDevice([in, annotation("_In_")] REFIID riid, [out, retval, annotation("_COM_Outptr_")] void** ppDevice);
}
[uuid(2411e7e1-12ac-4ccf-bd14-9798e8534dc0)]
interface IDXGIAdapter : IDXGIObject
{
INT32 EnumOutputs([in] UINT Output, [in, out] void* ppOutput);
HRESULT GetDesc([out, annotation("_Out_")] DXGI_ADAPTER_DESC* pDesc);
HRESULT CheckInterfaceSupport([in, annotation("_In_")] REFGUID InterfaceName,[out, annotation("_Out_")] LARGE_INTEGER* pUMDVersion);
};
[uuid(310d36a0-d2e7-4c0a-aa04-6a9d23b8886a)]
interface IDXGISwapChain : IDXGIDeviceSubObject
{
HRESULT Present([in] UINT SyncInterval, [in] UINT Flags);
HRESULT GetBuffer([in] UINT Buffer, [in, annotation("_In_")] REFIID riid, [in, out, annotation("_COM_Outptr_")] void** ppSurface);
HRESULT SetFullscreenState([in] BOOL Fullscreen, [in, annotation("_In_opt_")] IDXGIOutput* pTarget);
HRESULT GetFullscreenState([out, annotation("_Out_opt_")] BOOL* pFullscreen, [out, annotation("_COM_Outptr_opt_result_maybenull_")] IDXGIOutput** ppTarget);
HRESULT GetDesc([out] DXGI_SWAP_CHAIN_DESC* pDesc);
HRESULT ResizeBuffers([in] UINT BufferCount, [in] UINT Width, [in] UINT Height, [in] DXGI_FORMAT NewFormat, [in] UINT SwapChainFlags);
HRESULT ResizeTarget([in, annotation("_In_")] DXGI_MODE_DESC* pNewTargetParameters);
HRESULT GetContainingOutput([out, annotation("_COM_Outptr_")] IDXGIOutput** ppOutput);
HRESULT GetFrameStatistics([out, annotation("_Out_")] DXGI_FRAME_STATISTICS* pStats);
HRESULT GetLastPresentCount([out, annotation("_Out_")] UINT* pLastPresentCount);
}
[uuid(54ec77fa-1377-44e6-8c32-88fd5f44c84c)]
interface IDXGIDevice : IDXGIObject
{
HRESULT GetAdapter([out] IDXGIAdapter** pAdapter);
HRESULT CreateSurface([in, annotation("_In_")] DXGI_SURFACE_DESC* pDesc, [in] UINT NumSurfaces, [in] DXGI_USAGE Usage, [in, annotation("_In_opt_")] DXGI_SHARED_RESOURCE* pSharedResource, [out] IDXGISurface** ppSurface);
HRESULT QueryResourceResidency([in] IUnknown* ppResources, [out] DXGI_RESIDENCY* pResidencyStatus, [in] UINT NumResources);
HRESULT SetGPUThreadPriority([in] INT Priority);
HRESULT GetGPUThreadPriority([out, retval] INT* pPriority);
}
[uuid(ae02eedb-c735-4690-8d52-5a8dc20213aa)]
interface IDXGIOutput : IDXGIObject
{
HRESULT GetDesc([out] DXGI_OUTPUT_DESC* pDesc);
HRESULT GetDisplayModeList([in] DXGI_FORMAT EnumFormat, [in] UINT Flags, [in, out] UINT* pNumModes, [out] DXGI_MODE_DESC* pDesc);
HRESULT FindClosestMatchingMode([in, annotation("_In_")] DXGI_MODE_DESC* pModeToMatch, [out] DXGI_MODE_DESC* pClosestMatch, [in] IUnknown* pConcernedDevice);
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 SetDisplaySurface([in, annotation("_In_")] IDXGISurface* pScanoutSurface);
HRESULT GetDisplaySurfaceData([in, annotation("_In_")] IDXGISurface* pDestination);
HRESULT GetFrameStatistics([out, annotation("_Out_")] DXGI_FRAME_STATISTICS* pStats);
}
[uuid(cafcb56c-6ac3-4889-bf47-9e23bbd260ec)]
interface IDXGISurface : IDXGIDeviceSubObject
{
HRESULT GetDesc([out, annotation("_Out_")] DXGI_SURFACE_DESC* pDesc);
HRESULT Map([out, annotation("_Out_")] DXGI_MAPPED_RECT* pLockedRect, [in] UINT MapFlags);
HRESULT Unmap();
}
[uuid(770aae78-f26f-4dba-a829-253c83d1b387)]
interface IDXGIFactory1 : IDXGIFactory
{
HRESULT EnumAdapters1([in] UINT Adapter, [out, annotation("_COM_Outptr_")] IDXGIAdapter1** ppAdapter);
BOOL IsCurrent();
}
[uuid(29038f61-3839-4626-91fd-086879011a05)]
interface IDXGIAdapter1 : IDXGIAdapter
{
HRESULT GetDesc1([out, annotation("_Out_")] DXGI_ADAPTER_DESC1* pDesc);
}
[uuid(50c83a1c-e072-4c48-87b0-3630fa36a6d0)]
interface IDXGIFactory2 : IDXGIFactory1
{
BOOL IsWindowedStereoEnabled();
HRESULT CreateSwapChainForHwnd([in, annotation("_In_")] IUnknown* pDevice, [in, annotation("_In_")] HWND hWnd, [in, annotation("_In_")] DXGI_SWAP_CHAIN_DESC1* pDesc, [in, annotation("_In_opt_")] DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, [in, annotation("_In_opt_")] IDXGIOutput* pRestrictToOutput, [out, annotation("_COM_Outptr_")] IDXGISwapChain1** ppSwapChain);
HRESULT CreateSwapChainForCoreWindow([in, annotation("_In_")] IUnknown* pDevice, [in, annotation("_In_")] IUnknown* pWindow, [in, annotation("_In_")] DXGI_SWAP_CHAIN_DESC1* pDesc, [in, annotation("_In_opt_")] IDXGIOutput* pRestrictToOutput, [out, annotation("_COM_Outptr_")] IDXGISwapChain1** ppSwapChain);
HRESULT GetSharedResourceAdapterLuid([annotation("_In_")] HANDLE hResource, [annotation("_Out_")] LUID* pLuid);
HRESULT RegisterStereoStatusWindow([in, annotation("_In_")] HWND WindowHandle, [in, annotation("_In_")] UINT wMsg, [out, annotation("_Out_")] DWORD* pdwCookie);
HRESULT RegisterStereoStatusEvent([in, annotation("_In_")] HANDLE hEvent, [out, annotation("_Out_")] DWORD* pdwCookie);
void UnregisterStereoStatus([in, annotation("_In_")] DWORD dwCookie);
HRESULT RegisterOcclusionStatusWindow([in, annotation("_In_")] HWND WindowHandle, [in, annotation("_In_")] UINT wMsg, [out, annotation("_Out_")] DWORD* pdwCookie);
HRESULT RegisterOcclusionStatusEvent([in, annotation("_In_")] HANDLE hEvent, [out, annotation("_Out_")] DWORD* pdwCookie);
void UnregisterOcclusionStatus([in, annotation("_In_")] DWORD dwCookie);
HRESULT CreateSwapChainForComposition([in, annotation("_In_")] IUnknown* pDevice, [in, annotation("_In_")] DXGI_SWAP_CHAIN_DESC1* pDesc, [in, annotation("_In_opt_")] IDXGIOutput* pRestrictToOutput, [out, annotation("_COM_Outptr_")] IDXGISwapChain1** ppSwapChain);
}
[uuid(790a45f7-0d42-4876-983a-0a55cfe6f4aa)]
interface IDXGISwapChain1 : IDXGISwapChain
{
HRESULT GetDesc1([out, annotation("_Out_")] DXGI_SWAP_CHAIN_DESC1* pDesc);
HRESULT GetFullscreenDesc([out, annotation("_Out_")] DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc);
HRESULT GetHwnd([out, annotation("_Out_")] HWND* pHwnd);
HRESULT GetCoreWindow([in, annotation("_In_")] REFIID refiid, [out, annotation("_COM_Outptr_")] void** ppUnk);
HRESULT Present1([in] UINT SyncInterval, [in] UINT PresentFlags, [in, annotation("_In_")] DXGI_PRESENT_PARAMETERS* pPresentParameters);
BOOL IsTemporaryMonoSupported();
HRESULT GetRestrictToOutput([out, annotation("_Out_")] IDXGIOutput** ppRestrictToOutput);
HRESULT SetBackgroundColor([in, annotation("_In_")] DXGI_RGBA* pColor);
HRESULT GetBackgroundColor([out, annotation("_Out_")] DXGI_RGBA* pColor);
HRESULT SetRotation([in, annotation("_In_")] DXGI_MODE_ROTATION Rotation);
HRESULT GetRotation([out, annotation("_Out_")] DXGI_MODE_ROTATION* pRotation);
}

5
src/Windows/Avalonia.Win32/Win32GlManager.cs

@ -2,6 +2,7 @@ using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Win32.DxgiSwapchain;
using Avalonia.Win32.OpenGl;
using Avalonia.Win32.WinRT.Composition;
@ -49,6 +50,10 @@ namespace Avalonia.Win32
{
WinUICompositorConnection.TryCreateAndRegister(egl, opts.CompositionBackdropCornerRadius);
}
else if (opts.UseLowLatencyDxgiSwapChain)
{
DxgiConnection.TryCreateAndRegister(egl);
}
}
return egl;

10
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -96,6 +96,16 @@ namespace Avalonia
/// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app
/// </summary>
public float? CompositionBackdropCornerRadius { get; set; }
/// <summary>
/// When <see cref="UseLowLatencyDxgiSwapChain"/> is active, renders Avalonia through a low-latency Dxgi Swapchain.
/// Requires Feature Level 11_3 to be active, Windows 8.1+ Any Subversion.
/// This is only recommended if low input latency is desirable, and there is no need for the transparency
/// and stylings / blurrings offered by <see cref="UseWindowsUIComposition"/><br/>
/// This is mutually exclusive with
/// <see cref="UseWindowsUIComposition"/> which if active will override this setting.
/// </summary>
public bool UseLowLatencyDxgiSwapChain { get; set; } = false;
}
}

17
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -26,6 +26,7 @@ using static Avalonia.Win32.Interop.UnmanagedMethods;
using Avalonia.Collections.Pooled;
using Avalonia.Metadata;
using Avalonia.Platform.Storage;
using Avalonia.Win32.DxgiSwapchain;
namespace Avalonia.Win32
{
@ -63,6 +64,7 @@ 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;
@ -143,6 +145,16 @@ namespace Avalonia.Win32
egl.Display is AngleWin32EglDisplay angleDisplay &&
angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
DxgiConnection dxgiConnection = null;
if (!_isUsingComposition)
{
dxgiConnection = AvaloniaLocator.Current.GetService<DxgiConnection>();
_isUsingDxgiSwapchain = dxgiConnection is { } &&
glPlatform is EglPlatformOpenGlInterface eglDxgi &&
eglDxgi.Display is AngleWin32EglDisplay angleDisplayDxgi &&
angleDisplayDxgi.PlatformApi == AngleOptions.PlatformApi.DirectX11;
}
_wmPointerEnabled = Win32Platform.WindowsVersion >= PlatformConstants.Windows8;
CreateWindow();
@ -159,6 +171,11 @@ namespace Avalonia.Win32
_isUsingComposition = true;
}
else if (_isUsingDxgiSwapchain)
{
var dxgigl = new DxgiSwapchainWindow(dxgiConnection, this);
_gl = dxgigl;
}
else
{
if (glPlatform is EglPlatformOpenGlInterface egl2)

Loading…
Cancel
Save