測試代碼非常簡單,只是嘗試修改一個控件的背景色,讓界面不斷更新而已
以下是 MainWindow.axaml 代碼
<Window 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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="JowekukurNelaholefewhi.MainWindow"
Title="JowekukurNelaholefewhi"
ExtendClientAreaChromeHints="NoChrome"
Background="Transparent"
ExtendClientAreaToDecorationsHint="False">
<Border x:Name="BackgroundBorder">
<Button x:Name="ChangeTransparencyLevelHintButton" Width="200" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" Click="ChangeTransparencyLevelHintButton_OnClick"
Background="Blue">
<Button.Content>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="White">
Click to Change TransparencyLevelHint
</TextBlock>
</Button.Content>
</Button>
</Border>
</Window>
以下是 MainWindow.axaml.cs 代碼
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
RendererDiagnostics.DebugOverlays = RendererDebugOverlays.Fps;
Loaded += MainWindow_Loaded;
}
private async void MainWindow_Loaded(object? sender, RoutedEventArgs e)
{
while (IsLoaded)
{
await Task.Delay(10);
var color = new Color(0x02, NextByte(), NextByte(), NextByte());
BackgroundBorder.Background = new ImmutableSolidColorBrush(color);
}
static byte NextByte() => (byte) Random.Shared.Next(byte.MaxValue);
}
private void ChangeTransparencyLevelHintButton_OnClick(object? sender, RoutedEventArgs e)
{
if (TransparencyLevelHint.First() == WindowTransparencyLevel.Transparent)
{
TransparencyLevelHint = [WindowTransparencyLevel.AcrylicBlur];
}
else
{
TransparencyLevelHint = [WindowTransparencyLevel.Transparent];
}
}
}
核心是在 MainWindow_Loaded 裏面不斷刷新界面
本文所採用的測試代碼放在 github 和 gitee 上,可以使用如下命令行拉取代碼。我整個代碼倉庫比較龐大,使用以下命令行可以進行部分拉取,拉取速度比較快
先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行裏面輸入以下代碼,即可獲取到本文的代碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2599a486433e897590ba552cd9049a1bfcdf364f
以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼。如果依然拉取不到代碼,可以發郵件向我要代碼
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 2599a486433e897590ba552cd9049a1bfcdf364f
獲取代碼之後,進入 AvaloniaIDemo/JowekukurNelaholefewhi 文件夾,即可獲取到源代碼
不同的測試用例我沒有獨立代碼項目,只是通過 git 的 commit 區分
最近我在摸索 Avalonia 的渲染層,這個問題源自於 7 年前,我嘗試給 Avalonia 添加筆跡應用。在去年的時候,我發現 Avalonia 的筆跡性能非常糟糕,今年我設計了一個測試用例。在 Avalonia 窗口上疊加一個透明的 WPF 窗口,從 Avalonia 收到鼠標或觸摸輸入之後,再發送到 WPF 窗口上,讓 Avalonia 和 WPF 窗口同時對一個 Border 進行 RenderTransform 平移
此測試發現了 WPF 的渲染非常跟輸入,而 Avalonia 明顯落後
在我的測試用例裏面,特別讓 Avalonia 窗口去接收輸入,讓 Avalonia 驅動 WPF 的界面。如此可以排除 Avalonia 的輸入層帶來的延遲。完全只對比 Avalonia 和 WPF 的渲染層
詳細請參閲: https://github.com/AvaloniaUI/Avalonia/discussions/20562
實驗情況如下圖所示,藍色為 Avalonia 的控件,紅色是 WPF 的控件
為此,我嘗試了 Avalonia 的各個 Win32CompositionMode 來摸索渲染延遲。以下是我的實驗情況
本次實驗的機器配置如下:
- 屏幕: 3840x2160 (4K) + 百分百 DPI
- CPU: i5-12450H
- GPU: 集顯
LowLatencyDxgiSwapChain
測試代碼: https://github.com/lindexi/lindexi_gd/tree/2599a486433e897590ba552cd9049a1bfcdf364f/AvaloniaIDemo/JowekukurNelaholefewhi
測試結果:
- GPU: 佔用為 70-80 範圍
- DWM: 佔用為 1
- 幀率: 55-60 大部分時候靠近 60 幀率
WinUIComposition
測試代碼: https://github.com/lindexi/lindexi_gd/tree/8c3e57108cafaf5c6ab1c0b371e62f180ba62d9b/AvaloniaIDemo/JowekukurNelaholefewhi
- GPU: 佔用在 70 附近
- DWM: 佔用為 40 左右,可見合成過程中確實讓 GPU 非常繁忙
- 幀率: 20-30 幀
雖然 GPU 沒有吃滿,但是已經掉幀了,感覺這裏應該有坑
DirectComposition
測試代碼: https://github.com/lindexi/lindexi_gd/tree/6b2adfb85f1516663f128d0c8a3a7465069dbdfd/AvaloniaIDemo/JowekukurNelaholefewhi
- GPU: 佔用在 70 附近
- DWM: 佔用為 40 左右
- 幀率: 20-30 幀
可見 WinUIComposition 和 DirectComposition 的問題差不多
而 LowLatencyDxgiSwapChain 能夠獲取比較好的幀率,且對 DWM 佔用比較少