diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs index 22d125fede..c4034c7211 100644 --- a/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs +++ b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs @@ -48,7 +48,7 @@ namespace Avalonia.Win32.OpenGl { wglDeleteContext(_context); WglDCManager.ReleaseDC(_hWnd, _dc); - DestroyWindow(_hWnd); + WglDCManager.DestroyWindow(_hWnd); IsLost = true; } diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs b/src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs index e35e90c447..17c7fa62c6 100644 --- a/src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs +++ b/src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Avalonia.Win32.Interop; @@ -28,9 +29,22 @@ internal class WglDCManager 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() { @@ -49,12 +63,41 @@ internal class WglDCManager 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 WglDCManager() { + 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); new Thread(Worker) { IsBackground = true, @@ -62,6 +105,25 @@ internal class WglDCManager }.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(); @@ -87,6 +149,19 @@ internal class WglDCManager }); 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/WglDisplay.cs b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs index cac044e953..300e43e656 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,17 +43,8 @@ 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(); + Dispatcher.UIThread.VerifyAccess(); + _bootstrapWindow = WglDCManager.CreateOffscreenWindow(); _bootstrapDc = WglDCManager.GetDC(_bootstrapWindow); _defaultPfd = new PixelFormatDescriptor { @@ -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,7 +115,7 @@ namespace Avalonia.Win32.OpenGl using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null)) { - var window = CreateOffscreenWindow(); + var window = WglDCManager.CreateOffscreenWindow(); var dc = WglDCManager.GetDC(window); SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd); foreach (var version in versions) @@ -165,25 +149,12 @@ namespace Avalonia.Win32.OpenGl } WglDCManager.ReleaseDC(window, dc); - DestroyWindow(window); + WglDCManager.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); + } }