Browse Source

Make WglDCManager to also manage offscreen windows

pull/9639/head
Nikita Tsukanov 4 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); wglDeleteContext(_context);
WglDCManager.ReleaseDC(_hWnd, _dc); WglDCManager.ReleaseDC(_hWnd, _dc);
DestroyWindow(_hWnd); WglDCManager.DestroyWindow(_hWnd);
IsLost = true; IsLost = true;
} }

75
src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
@ -28,9 +29,22 @@ internal class WglDCManager
public IntPtr DC; public IntPtr DC;
public TaskCompletionSource<object> Result; 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 Queue<object> s_Queue = new();
private static readonly AutoResetEvent s_Event = new(false); 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() static void Worker()
{ {
@ -49,12 +63,41 @@ internal class WglDCManager
UnmanagedMethods.ReleaseDC(releaseDc.Window, releaseDc.DC); UnmanagedMethods.ReleaseDC(releaseDc.Window, releaseDc.DC);
releaseDc.Result.SetResult(null); 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() 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) new Thread(Worker)
{ {
IsBackground = true, IsBackground = true,
@ -62,6 +105,25 @@ internal class WglDCManager
}.Start(); }.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) public static IntPtr GetDC(IntPtr hWnd)
{ {
var tcs = new TaskCompletionSource<IntPtr>(); var tcs = new TaskCompletionSource<IntPtr>();
@ -87,6 +149,19 @@ internal class WglDCManager
}); });
s_Event.Set(); s_Event.Set();
tcs.Task.Wait(); 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;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.Threading;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using static Avalonia.Win32.Interop.UnmanagedMethods; using static Avalonia.Win32.Interop.UnmanagedMethods;
using static Avalonia.Win32.OpenGl.WglConsts; using static Avalonia.Win32.OpenGl.WglConsts;
@ -9,8 +10,6 @@ namespace Avalonia.Win32.OpenGl
internal class WglDisplay internal class WglDisplay
{ {
private static bool? _initialized; private static bool? _initialized;
private static ushort _windowClass;
private static readonly WndProc _wndProcDelegate = WndProc;
private static readonly DebugCallbackDelegate _debugCallback = DebugCallback; private static readonly DebugCallbackDelegate _debugCallback = DebugCallback;
private static IntPtr _bootstrapContext; private static IntPtr _bootstrapContext;
@ -44,17 +43,8 @@ namespace Avalonia.Win32.OpenGl
} }
static bool InitializeCore() static bool InitializeCore()
{ {
var wndClassEx = new WNDCLASSEX Dispatcher.UIThread.VerifyAccess();
{ _bootstrapWindow = WglDCManager.CreateOffscreenWindow();
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = _wndProcDelegate,
lpszClassName = "AvaloniaGlWindow-" + Guid.NewGuid(),
style = (int)ClassStyles.CS_OWNDC
};
_windowClass = RegisterClassEx(ref wndClassEx);
_bootstrapWindow = CreateOffscreenWindow();
_bootstrapDc = WglDCManager.GetDC(_bootstrapWindow); _bootstrapDc = WglDCManager.GetDC(_bootstrapWindow);
_defaultPfd = new PixelFormatDescriptor _defaultPfd = new PixelFormatDescriptor
{ {
@ -105,17 +95,11 @@ namespace Avalonia.Win32.OpenGl
DescribePixelFormat(_bootstrapDc, formats[0], Marshal.SizeOf<PixelFormatDescriptor>(), ref _defaultPfd); DescribePixelFormat(_bootstrapDc, formats[0], Marshal.SizeOf<PixelFormatDescriptor>(), ref _defaultPfd);
_defaultPixelFormat = formats[0]; _defaultPixelFormat = formats[0];
} }
wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); wglMakeCurrent(IntPtr.Zero, IntPtr.Zero);
return true; 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) private static void DebugCallback(int source, int type, int id, int severity, int len, IntPtr message, IntPtr userparam)
{ {
var err = Marshal.PtrToStringAnsi(message, len); var err = Marshal.PtrToStringAnsi(message, len);
@ -131,7 +115,7 @@ namespace Avalonia.Win32.OpenGl
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null)) using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null))
{ {
var window = CreateOffscreenWindow(); var window = WglDCManager.CreateOffscreenWindow();
var dc = WglDCManager.GetDC(window); var dc = WglDCManager.GetDC(window);
SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd); SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd);
foreach (var version in versions) foreach (var version in versions)
@ -165,25 +149,12 @@ namespace Avalonia.Win32.OpenGl
} }
WglDCManager.ReleaseDC(window, dc); WglDCManager.ReleaseDC(window, dc);
DestroyWindow(window); WglDCManager.DestroyWindow(window);
return null; 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