Browse Source

Fixed WGL HDC management

pull/9639/head
Nikita Tsukanov 3 years ago
parent
commit
7ff602e471
  1. 2
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  2. 4
      src/Windows/Avalonia.Win32/OpenGl/WglContext.cs
  3. 88
      src/Windows/Avalonia.Win32/OpenGl/WglDCManager.cs
  4. 6
      src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs
  5. 2
      src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs
  6. 5
      src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs

2
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1627,7 +1627,7 @@ namespace Avalonia.Win32.Interop
public static extern bool wglDeleteContext(IntPtr context);
[DllImport("opengl32.dll")]
[DllImport("opengl32.dll", SetLastError = true)]
public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context);
[DllImport("opengl32.dll")]

4
src/Windows/Avalonia.Win32/OpenGl/WglContext.cs

@ -46,7 +46,7 @@ namespace Avalonia.Win32.OpenGl
public void Dispose()
{
wglDeleteContext(_context);
ReleaseDC(_hWnd, _dc);
WglDCManager.ReleaseDC(_hWnd, _dc);
DestroyWindow(_hWnd);
IsLost = true;
}
@ -72,7 +72,7 @@ namespace Avalonia.Win32.OpenGl
public IntPtr CreateConfiguredDeviceContext(IntPtr hWnd)
{
var dc = GetDC(hWnd);
var dc = WglDCManager.GetDC(hWnd);
var fmt = _formatDescriptor;
SetPixelFormat(dc, _pixelFormat, ref fmt);
return dc;

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

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32.OpenGl;
/// <summary>
/// 1) ReleaseDC can only happen from the same thread that has called GetDC
/// 2) When thread exits all of its HDCs are getting destroyed
/// 3) We need to create OpenGL render targets from thread pool threads
///
/// So this class hosts a dedicated thread for managing HDCs for OpenGL
/// </summary>
internal class WglDCManager
{
class GetDCOp
{
public IntPtr Window;
public TaskCompletionSource<IntPtr> Result;
}
class ReleaseDCOp
{
public IntPtr Window;
public IntPtr DC;
public TaskCompletionSource<object> Result;
}
private static readonly Queue<object> s_Queue = new();
private static readonly AutoResetEvent s_Event = new(false);
static void Worker()
{
while (true)
{
s_Event.WaitOne();
lock (s_Queue)
{
if(s_Queue.Count == 0)
continue;
var job = s_Queue.Dequeue();
if (job is GetDCOp getDc)
getDc.Result.TrySetResult(UnmanagedMethods.GetDC(getDc.Window));
else if (job is ReleaseDCOp releaseDc)
{
UnmanagedMethods.ReleaseDC(releaseDc.Window, releaseDc.DC);
releaseDc.Result.SetResult(null);
}
}
}
}
static WglDCManager()
{
new Thread(Worker) { IsBackground = true }.Start();
}
public static IntPtr GetDC(IntPtr hWnd)
{
var tcs = new TaskCompletionSource<IntPtr>();
lock(s_Queue)
s_Queue.Enqueue(new GetDCOp
{
Window = hWnd,
Result = tcs
});
s_Event.Set();
return tcs.Task.Result;
}
public static void ReleaseDC(IntPtr hWnd, IntPtr hDC)
{
var tcs = new TaskCompletionSource<object>();
lock(s_Queue)
s_Queue.Enqueue(new ReleaseDCOp()
{
Window = hWnd,
DC = hDC,
Result = tcs
});
s_Event.Set();
tcs.Task.Wait();
}
}

6
src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs

@ -55,7 +55,7 @@ namespace Avalonia.Win32.OpenGl
_windowClass = RegisterClassEx(ref wndClassEx);
_bootstrapWindow = CreateOffscreenWindow();
_bootstrapDc = GetDC(_bootstrapWindow);
_bootstrapDc = WglDCManager.GetDC(_bootstrapWindow);
_defaultPfd = new PixelFormatDescriptor
{
Size = (ushort)Marshal.SizeOf<PixelFormatDescriptor>(),
@ -132,7 +132,7 @@ namespace Avalonia.Win32.OpenGl
using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null))
{
var window = CreateOffscreenWindow();
var dc = GetDC(window);
var dc = WglDCManager.GetDC(window);
SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd);
foreach (var version in versions)
{
@ -159,7 +159,7 @@ namespace Avalonia.Win32.OpenGl
_defaultPixelFormat, _defaultPfd);
}
ReleaseDC(window, dc);
WglDCManager.ReleaseDC(window, dc);
DestroyWindow(window);
return null;
}

2
src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs

@ -37,7 +37,7 @@ namespace Avalonia.Win32.OpenGl
public void Dispose()
{
UnmanagedMethods.ReleaseDC(_hdc, _info.Handle);
WglDCManager.ReleaseDC(_info.Handle, _hdc);
}
public IGlPlatformSurfaceRenderingSession BeginDraw()

5
src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.OpenGL;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@ -22,9 +23,11 @@ namespace Avalonia.Win32.OpenGl
if (!wglMakeCurrent(gc, context))
{
var lastError = Marshal.GetLastWin32Error();
var caps = GetDeviceCaps(gc, (DEVICECAP)12);
if(monitor != null && takeMonitor)
Monitor.Exit(monitor);
throw new OpenGlException("Unable to make the context current");
throw new OpenGlException($"Unable to make the context current: {lastError}, DC valid: {caps != 0}");
}
}

Loading…
Cancel
Save