Fix the DxgiRenderTimerLoop thread get the screens from UI thread (#20511)

Fixes https://github.com/AvaloniaUI/Avalonia/issues/20508
This commit is contained in:
lindexi
2026-01-21 16:32:25 +08:00
committed by GitHub
parent 0fb64d65cc
commit 93eeadf3c1
2 changed files with 49 additions and 4 deletions
@@ -1,13 +1,20 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.OpenGL.Egl;
using Avalonia.Rendering;
using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods;
using MicroCom.Runtime;
using Windows.Win32;
using Windows.Win32.Graphics.Gdi;
using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods;
using static Avalonia.Win32.Interop.UnmanagedMethods;
namespace Avalonia.Win32.DirectX
{
internal unsafe class DxgiConnection : IRenderTimer, IWindowsSurfaceFactory
@@ -111,6 +118,8 @@ namespace Avalonia.Win32.DirectX
ushort adapterIndex = 0;
Dictionary<HMONITOR /*MonitorHandler*/, uint /*Frequency*/> monitorFrequencies = GetAllMonitorFrequencies();
// this looks odd, but that's just how one enumerates adapters in DXGI
while (fact.EnumAdapters(adapterIndex, &adapterPointer) == 0)
{
@@ -122,8 +131,12 @@ namespace Avalonia.Win32.DirectX
using var output = MicroComRuntime.CreateProxyFor<IDXGIOutput>(outputPointer, true);
DXGI_OUTPUT_DESC outputDesc = output.Desc;
var screen = Win32Platform.Instance.Screen.ScreenFromHMonitor((IntPtr)outputDesc.Monitor.Value);
var frequency = screen?.Frequency ?? highestRefreshRate;
var hMonitor = new HMONITOR(outputDesc.Monitor.Value);
var frequency =
monitorFrequencies.TryGetValue(hMonitor, out uint frequencyValue) ?
frequencyValue :
highestRefreshRate;
if (highestRefreshRate < frequency)
{
@@ -145,6 +158,33 @@ namespace Avalonia.Win32.DirectX
}
private unsafe Dictionary<HMONITOR /*MonitorHandler*/, uint /*Frequency*/> GetAllMonitorFrequencies()
{
var monitorHandlers = ScreenImpl.GetAllDisplayMonitorHandlers();
var dictionary = new Dictionary<HMONITOR /*MonitorHandler*/, uint /*Frequency*/>(monitorHandlers.Count);
foreach (var monitorHandler in monitorHandlers)
{
var info = MONITORINFOEX.Create();
var hMonitor = new HMONITOR(monitorHandler);
PInvoke.GetMonitorInfo(hMonitor, (MONITORINFO*)&info);
var deviceMode = new DEVMODEW
{
dmFields = DEVMODE_FIELD_FLAGS.DM_DISPLAYORIENTATION | DEVMODE_FIELD_FLAGS.DM_DISPLAYFREQUENCY,
dmSize = (ushort)Marshal.SizeOf<DEVMODEW>()
};
PInvoke.EnumDisplaySettings(info.szDevice.ToString(), ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS,
ref deviceMode);
var frequency = deviceMode.dmDisplayFrequency;
dictionary[hMonitor] = frequency;
}
return dictionary;
}
// Used the windows composition as a blueprint for this startup/creation
private static bool TryCreateAndRegisterCore()
{
+5
View File
@@ -15,6 +15,11 @@ internal unsafe class ScreenImpl : ScreensBase<nint, WinScreen>
protected override int GetScreenCount() => GetSystemMetrics(SystemMetric.SM_CMONITORS);
protected override IReadOnlyList<nint> GetAllScreenKeys()
{
return GetAllDisplayMonitorHandlers();
}
public static List<nint> GetAllDisplayMonitorHandlers()
{
var screens = new List<nint>();
var gcHandle = GCHandle.Alloc(screens);