Browse Source

Make WglDCManager to also manage offscreen windows

pull/9639/head
Nikita Tsukanov 3 years ago
parent
commit
30db969e36
  1. 2
      src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
  2. 75
      src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs
  3. 45
      src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs

2
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;
}

75
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<object> Result;
}
class CreateWindowOp
{
public TaskCompletionSource<IntPtr> Result;
}
class DestroyWindowOp
{
public IntPtr Window;
public TaskCompletionSource<object> Result;
}
private static readonly Queue<object> 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<UnmanagedMethods.WNDCLASSEX>(),
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<IntPtr>();
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<IntPtr>();
@ -87,6 +149,19 @@ internal class WglDCManager
});
s_Event.Set();
tcs.Task.Wait();
}
public static void DestroyWindow(IntPtr hWnd)
{
var tcs = new TaskCompletionSource<object>();
lock(s_Queue)
s_Queue.Enqueue(new DestroyWindowOp()
{
Window = hWnd,
Result = tcs
});
s_Event.Set();
tcs.Task.Wait();
}
}

45
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<WNDCLASSEX>(),
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<PixelFormatDescriptor>(), 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);
}
}

Loading…
Cancel
Save