diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index 233160b025..340ccdae19 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -18,6 +18,10 @@ namespace RenderDemo // App configuration, used by the entry point and previewer static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() + .With(new Win32PlatformOptions + { + OverlayPopups = true, + }) .UsePlatformDetect() .UseReactiveUI() .LogToDebug(); diff --git a/src/Avalonia.Base/AvaloniaLocator.cs b/src/Avalonia.Base/AvaloniaLocator.cs index f9bbe38bec..3163d15c1b 100644 --- a/src/Avalonia.Base/AvaloniaLocator.cs +++ b/src/Avalonia.Base/AvaloniaLocator.cs @@ -54,6 +54,23 @@ namespace Avalonia return _locator; } + public AvaloniaLocator ToLazy(Func func) where TImlp : TService + { + var constructed = false; + TImlp instance = default; + _locator._registry[typeof (TService)] = () => + { + if (!constructed) + { + instance = func(); + constructed = true; + } + + return instance; + }; + return _locator; + } + public AvaloniaLocator ToSingleton() where TImpl : class, TService, new() { TImpl instance = null; diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index b7c68c4b95..5feb6c9e46 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1011,6 +1011,10 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool InvalidateRect(IntPtr hWnd, RECT* lpRect, bool bErase); + + + [DllImport("user32.dll")] + public static extern bool ValidateRect(IntPtr hWnd, IntPtr lpRect); [DllImport("user32.dll")] public static extern bool IsWindowEnabled(IntPtr hWnd); @@ -1292,6 +1296,41 @@ namespace Avalonia.Win32.Interop [DllImport("gdi32.dll")] public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject); + [DllImport("gdi32.dll")] + public static extern int ChoosePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); + + [DllImport("gdi32.dll")] + public static extern int DescribePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); + + [DllImport("gdi32.dll")] + public static extern int SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PixelFormatDescriptor pfd); + + + [DllImport("gdi32.dll")] + public static extern int DescribePixelFormat(IntPtr hdc, int iPixelFormat, int bytes, ref PixelFormatDescriptor pfd); + + [DllImport("gdi32.dll")] + public static extern bool SwapBuffers(IntPtr hdc); + + [DllImport("opengl32.dll")] + public static extern IntPtr wglCreateContext(IntPtr hdc); + + [DllImport("opengl32.dll")] + public static extern bool wglDeleteContext(IntPtr context); + + + [DllImport("opengl32.dll")] + public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context); + + [DllImport("opengl32.dll")] + public static extern IntPtr wglGetCurrentContext(); + + [DllImport("opengl32.dll")] + public static extern IntPtr wglGetCurrentDC(); + + [DllImport("opengl32.dll", CharSet = CharSet.Ansi)] + public static extern IntPtr wglGetProcAddress(string name); + [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, @@ -2121,4 +2160,57 @@ namespace Avalonia.Win32.Interop public bool fNC; public bool fWide; } + + [Flags] + internal enum PixelFormatDescriptorFlags : uint + { + PFD_DOUBLEBUFFER = 0x00000001, + PFD_STEREO = 0x00000002, + PFD_DRAW_TO_WINDOW = 0x00000004, + PFD_DRAW_TO_BITMAP = 0x00000008, + PFD_SUPPORT_GDI = 0x00000010, + PFD_SUPPORT_OPENGL = 0x00000020, + PFD_GENERIC_FORMAT = 0x00000040, + PFD_NEED_PALETTE = 0x00000080, + PFD_NEED_SYSTEM_PALETTE = 0x00000100, + PFD_SWAP_EXCHANGE = 0x00000200, + PFD_SWAP_COPY = 0x00000400, + PFD_SWAP_LAYER_BUFFERS = 0x00000800, + PFD_GENERIC_ACCELERATED = 0x00001000, + PFD_SUPPORT_DIRECTDRAW = 0x00002000, + PFD_DEPTH_DONTCARE = 0x20000000, + PFD_DOUBLEBUFFER_DONTCARE = 0x40000000, + PFD_STEREO_DONTCARE = 0x80000000, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PixelFormatDescriptor + { + public ushort Size; + public ushort Version; + public PixelFormatDescriptorFlags Flags; + public byte PixelType; + public byte ColorBits; + public byte RedBits; + public byte RedShift; + public byte GreenBits; + public byte GreenShift; + public byte BlueBits; + public byte BlueShift; + public byte AlphaBits; + public byte AlphaShift; + public byte AccumBits; + public byte AccumRedBits; + public byte AccumGreenBits; + public byte AccumBlueBits; + public byte AccumAlphaBits; + public byte DepthBits; + public byte StencilBits; + public byte AuxBuffers; + public byte LayerType; + private byte Reserved; + public uint LayerMask; + public uint VisibleMask; + public uint DamageMask; + } } diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglConsts.cs b/src/Windows/Avalonia.Win32/OpenGl/WglConsts.cs new file mode 100644 index 0000000000..3cb3dc25bb --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglConsts.cs @@ -0,0 +1,62 @@ +namespace Avalonia.Win32.OpenGl +{ + internal class WglConsts + { + public const int WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; + public const int WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; + public const int WGL_CONTEXT_LAYER_PLANE_ARB = 0x2093; + public const int WGL_CONTEXT_FLAGS_ARB = 0x2094; + public const int WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; + public const int WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000; + + public const int WGL_DRAW_TO_WINDOW_ARB = 0x2001; + public const int WGL_DRAW_TO_BITMAP_ARB = 0x2002; + public const int WGL_ACCELERATION_ARB = 0x2003; + public const int WGL_NEED_PALETTE_ARB = 0x2004; + public const int WGL_NEED_SYSTEM_PALETTE_ARB = 0x2005; + public const int WGL_SWAP_LAYER_BUFFERS_ARB = 0x2006; + public const int WGL_SWAP_METHOD_ARB = 0x2007; + public const int WGL_NUMBER_OVERLAYS_ARB = 0x2008; + public const int WGL_NUMBER_UNDERLAYS_ARB = 0x2009; + public const int WGL_TRANSPARENT_ARB = 0x200A; + public const int WGL_TRANSPARENT_RED_VALUE_ARB = 0x2037; + public const int WGL_TRANSPARENT_GREEN_VALUE_ARB = 0x2038; + public const int WGL_TRANSPARENT_BLUE_VALUE_ARB = 0x2039; + public const int WGL_TRANSPARENT_ALPHA_VALUE_ARB = 0x203A; + public const int WGL_TRANSPARENT_INDEX_VALUE_ARB = 0x203B; + public const int WGL_SHARE_DEPTH_ARB = 0x200C; + public const int WGL_SHARE_STENCIL_ARB = 0x200D; + public const int WGL_SHARE_ACCUM_ARB = 0x200E; + public const int WGL_SUPPORT_GDI_ARB = 0x200F; + public const int WGL_SUPPORT_OPENGL_ARB = 0x2010; + public const int WGL_DOUBLE_BUFFER_ARB = 0x2011; + public const int WGL_STEREO_ARB = 0x2012; + public const int WGL_PIXEL_TYPE_ARB = 0x2013; + public const int WGL_COLOR_BITS_ARB = 0x2014; + public const int WGL_RED_BITS_ARB = 0x2015; + public const int WGL_RED_SHIFT_ARB = 0x2016; + public const int WGL_GREEN_BITS_ARB = 0x2017; + public const int WGL_GREEN_SHIFT_ARB = 0x2018; + public const int WGL_BLUE_BITS_ARB = 0x2019; + public const int WGL_BLUE_SHIFT_ARB = 0x201A; + public const int WGL_ALPHA_BITS_ARB = 0x201B; + public const int WGL_ALPHA_SHIFT_ARB = 0x201C; + public const int WGL_ACCUM_BITS_ARB = 0x201D; + public const int WGL_ACCUM_RED_BITS_ARB = 0x201E; + public const int WGL_ACCUM_GREEN_BITS_ARB = 0x201F; + public const int WGL_ACCUM_BLUE_BITS_ARB = 0x2020; + public const int WGL_ACCUM_ALPHA_BITS_ARB = 0x2021; + public const int WGL_DEPTH_BITS_ARB = 0x2022; + public const int WGL_STENCIL_BITS_ARB = 0x2023; + public const int WGL_AUX_BUFFERS_ARB = 0x2024; + public const int WGL_NO_ACCELERATION_ARB = 0x2025; + public const int WGL_GENERIC_ACCELERATION_ARB = 0x2026; + public const int WGL_FULL_ACCELERATION_ARB = 0x2027; + public const int WGL_SWAP_EXCHANGE_ARB = 0x2028; + public const int WGL_SWAP_COPY_ARB = 0x2029; + public const int WGL_SWAP_UNDEFINED_ARB = 0x202A; + public const int WGL_TYPE_RGBA_ARB = 0x202B; + public const int WGL_TYPE_COLORINDEX_ARB = 0x202C; + + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs new file mode 100644 index 0000000000..d6633ddb61 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglContext.cs @@ -0,0 +1,85 @@ +using System; +using System.Reactive.Disposables; +using Avalonia.OpenGL; +using Avalonia.Win32.Interop; +using static Avalonia.Win32.Interop.UnmanagedMethods; +using static Avalonia.Win32.OpenGl.WglConsts; + +namespace Avalonia.Win32.OpenGl +{ + class WglContext : IGlContext + { + private object _lock = new object(); + private readonly WglContext _sharedWith; + private readonly IntPtr _context; + private readonly IntPtr _hWnd; + private readonly IntPtr _dc; + private readonly int _pixelFormat; + private readonly PixelFormatDescriptor _formatDescriptor; + public IntPtr Handle => _context; + + public WglContext(WglContext sharedWith, GlVersion version, IntPtr context, IntPtr hWnd, IntPtr dc, int pixelFormat, + PixelFormatDescriptor formatDescriptor) + { + Version = version; + _sharedWith = sharedWith; + _context = context; + _hWnd = hWnd; + _dc = dc; + _pixelFormat = pixelFormat; + _formatDescriptor = formatDescriptor; + StencilSize = formatDescriptor.StencilBits; + using (MakeCurrent()) + GlInterface = new GlInterface(version, proc => + { + var ext = wglGetProcAddress(proc); + if (ext != IntPtr.Zero) + return ext; + return GetProcAddress(WglDisplay.OpenGl32Handle, proc); + }); + + } + + public void Dispose() + { + wglDeleteContext(_context); + ReleaseDC(_hWnd, _dc); + DestroyWindow(_hWnd); + } + + public GlVersion Version { get; } + public GlInterface GlInterface { get; } + public int SampleCount { get; } + public int StencilSize { get; } + + private bool IsCurrent => wglGetCurrentContext() == _context && wglGetCurrentDC() == _dc; + public IDisposable MakeCurrent() + { + if(IsCurrent) + return Disposable.Empty; + return new WglRestoreContext(_dc, _context, _lock); + } + + public IDisposable EnsureCurrent() => MakeCurrent(); + + + public IntPtr CreateConfiguredDeviceContext(IntPtr hWnd) + { + var dc = GetDC(hWnd); + var fmt = _formatDescriptor; + SetPixelFormat(dc, _pixelFormat, ref fmt); + return dc; + } + + public IDisposable MakeCurrent(IntPtr hdc) => new WglRestoreContext(hdc, _context, _lock); + + public bool IsSharedWith(IGlContext context) + { + var c = (WglContext)context; + return c == this + || c._sharedWith == this + || _sharedWith == context + || _sharedWith != null && _sharedWith == c._sharedWith; + } + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs new file mode 100644 index 0000000000..0d387bdb2c --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglDisplay.cs @@ -0,0 +1,183 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.OpenGL; +using Avalonia.Win32.Interop; +using static Avalonia.Win32.Interop.UnmanagedMethods; +using static Avalonia.Win32.OpenGl.WglConsts; +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; + private static IntPtr _bootstrapWindow; + private static IntPtr _bootstrapDc; + private static PixelFormatDescriptor _defaultPfd; + private static int _defaultPixelFormat; + public static IntPtr OpenGl32Handle = LoadLibrary("opengl32"); + + private delegate bool WglChoosePixelFormatARBDelegate(IntPtr hdc, int[] piAttribIList, float[] pfAttribFList, + int nMaxFormats, int[] piFormats, out int nNumFormats); + + private static WglChoosePixelFormatARBDelegate WglChoosePixelFormatArb; + + private delegate IntPtr WglCreateContextAttribsARBDelegate(IntPtr hDC, IntPtr hShareContext, int[] attribList); + + private static WglCreateContextAttribsARBDelegate WglCreateContextAttribsArb; + + private delegate void GlDebugMessageCallbackDelegate(IntPtr callback, IntPtr userParam); + + private static GlDebugMessageCallbackDelegate GlDebugMessageCallback; + + private delegate void DebugCallbackDelegate(int source, int type, int id, int severity, int len, IntPtr message, + IntPtr userParam); + + static bool Initialize() + { + if (!_initialized.HasValue) + _initialized = InitializeCore(); + return _initialized.Value; + } + 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(); + _bootstrapDc = GetDC(_bootstrapWindow); + _defaultPfd = new PixelFormatDescriptor + { + Size = (ushort)Marshal.SizeOf(), + Version = 1, + Flags = PixelFormatDescriptorFlags.PFD_DRAW_TO_WINDOW | + PixelFormatDescriptorFlags.PFD_SUPPORT_OPENGL | PixelFormatDescriptorFlags.PFD_DOUBLEBUFFER, + DepthBits = 24, + StencilBits = 8, + ColorBits = 32 + }; + _defaultPixelFormat = ChoosePixelFormat(_bootstrapDc, ref _defaultPfd); + SetPixelFormat(_bootstrapDc, _defaultPixelFormat, ref _defaultPfd); + + _bootstrapContext = wglCreateContext(_bootstrapDc); + if (_bootstrapContext == IntPtr.Zero) + return false; + + wglMakeCurrent(_bootstrapDc, _bootstrapContext); + WglCreateContextAttribsArb = Marshal.GetDelegateForFunctionPointer( + wglGetProcAddress("wglCreateContextAttribsARB")); + + WglChoosePixelFormatArb = + Marshal.GetDelegateForFunctionPointer( + wglGetProcAddress("wglChoosePixelFormatARB")); + + GlDebugMessageCallback = + Marshal.GetDelegateForFunctionPointer( + wglGetProcAddress("glDebugMessageCallback")); + + + var formats = new int[1]; + WglChoosePixelFormatArb(_bootstrapDc, new int[] + { + WGL_DRAW_TO_WINDOW_ARB, 1, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_SUPPORT_OPENGL_ARB, 1, + WGL_DOUBLE_BUFFER_ARB, 1, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, 32, + WGL_DEPTH_BITS_ARB, 0, + WGL_STENCIL_BITS_ARB, 0, + 0, // End + }, null, 1, formats, out int numFormats); + if (numFormats != 0) + { + 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); + Console.Error.WriteLine(err); + } + + public static WglContext CreateContext(GlVersion[] versions, IGlContext share) + { + if (!Initialize()) + return null; + + var shareContext = (WglContext)share; + + using (new WglRestoreContext(_bootstrapDc, _bootstrapContext, null)) + { + var window = CreateOffscreenWindow(); + var dc = GetDC(window); + SetPixelFormat(dc, _defaultPixelFormat, ref _defaultPfd); + foreach (var version in versions) + { + if(version.Type != GlProfileType.OpenGL) + continue; + var context = WglCreateContextAttribsArb(dc, shareContext?.Handle ?? IntPtr.Zero, + new[] + { + // major + WGL_CONTEXT_MAJOR_VERSION_ARB, version.Major, + // minor + WGL_CONTEXT_MINOR_VERSION_ARB, version.Minor, + // core profile + WGL_CONTEXT_PROFILE_MASK_ARB, 1, + // debug + WGL_CONTEXT_FLAGS_ARB, 1, + // end + 0, 0 + }); + using(new WglRestoreContext(dc, context, null)) + GlDebugMessageCallback(Marshal.GetFunctionPointerForDelegate(_debugCallback), IntPtr.Zero); + if (context != IntPtr.Zero) + return new WglContext(shareContext, version, context, window, dc, + _defaultPixelFormat, _defaultPfd); + } + + ReleaseDC(window, dc); + 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); + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs new file mode 100644 index 0000000000..72bcffd447 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglGlPlatformSurface.cs @@ -0,0 +1,85 @@ +using System; +using System.Diagnostics; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Egl; +using Avalonia.OpenGL.Surfaces; +using Avalonia.Win32.Interop; +using static Avalonia.OpenGL.GlConsts; +using static Avalonia.Win32.Interop.UnmanagedMethods; +namespace Avalonia.Win32.OpenGl +{ + class WglGlPlatformSurface: IGlPlatformSurface + { + + private readonly WglContext _context; + private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; + + public WglGlPlatformSurface(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) + { + _context = context; + _info = info; + } + + public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() + { + return new RenderTarget(_context, _info); + } + + class RenderTarget : IGlPlatformSurfaceRenderTarget + { + private readonly WglContext _context; + private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; + private IntPtr _hdc; + public RenderTarget(WglContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) + { + _context = context; + _info = info; + _hdc = context.CreateConfiguredDeviceContext(info.Handle); + } + + public void Dispose() + { + UnmanagedMethods.ReleaseDC(_hdc, _info.Handle); + } + + public IGlPlatformSurfaceRenderingSession BeginDraw() + { + var oldContext = _context.MakeCurrent(_hdc); + + // Reset to default FBO first + _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0); + + return new Session(_context, _hdc, _info, oldContext); + } + + class Session : IGlPlatformSurfaceRenderingSession + { + private readonly WglContext _context; + private readonly IntPtr _hdc; + private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; + private readonly IDisposable _clearContext; + public IGlContext Context => _context; + + public Session(WglContext context, IntPtr hdc, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, + IDisposable clearContext) + { + _context = context; + _hdc = hdc; + _info = info; + _clearContext = clearContext; + } + + public void Dispose() + { + _context.GlInterface.Flush(); + UnmanagedMethods.SwapBuffers(_hdc); + _clearContext.Dispose(); + } + + public PixelSize Size => _info.Size; + public double Scaling => _info.Scaling; + public bool IsYFlipped { get; } + } + } + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs new file mode 100644 index 0000000000..b948495b99 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglPlatformOpenGlInterface.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using Avalonia.Logging; +using Avalonia.OpenGL; + +namespace Avalonia.Win32.OpenGl +{ + class WglPlatformOpenGlInterface : IPlatformOpenGlInterface + { + public WglContext PrimaryContext { get; } + IGlContext IPlatformOpenGlInterface.PrimaryContext => PrimaryContext; + public IGlContext CreateSharedContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, PrimaryContext); + + public bool CanShareContexts => true; + public bool CanCreateContexts => true; + public IGlContext CreateContext() => WglDisplay.CreateContext(new[] { PrimaryContext.Version }, null); + + private WglPlatformOpenGlInterface(WglContext primary) + { + PrimaryContext = primary; + } + + public static WglPlatformOpenGlInterface TryCreate() + { + try + { + var opts = AvaloniaLocator.Current.GetService() ?? new Win32PlatformOptions(); + var primary = WglDisplay.CreateContext(opts.WglProfiles.ToArray(), null); + return new WglPlatformOpenGlInterface(primary); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("WGL", "Unable to initialize WGL: " + e); + } + + return null; + } + } +} diff --git a/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs b/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs new file mode 100644 index 0000000000..265f078a5c --- /dev/null +++ b/src/Windows/Avalonia.Win32/OpenGl/WglRestoreContext.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using Avalonia.OpenGL; +using static Avalonia.Win32.Interop.UnmanagedMethods; + +namespace Avalonia.Win32.OpenGl +{ + internal class WglRestoreContext : IDisposable + { + private readonly object _monitor; + private readonly IntPtr _oldDc; + private readonly IntPtr _oldContext; + + public WglRestoreContext(IntPtr gc, IntPtr context, object monitor, bool takeMonitor = true) + { + _monitor = monitor; + _oldDc = wglGetCurrentDC(); + _oldContext = wglGetCurrentContext(); + + if (monitor != null && takeMonitor) + Monitor.Enter(monitor); + + if (!wglMakeCurrent(gc, context)) + { + if(monitor != null && takeMonitor) + Monitor.Exit(monitor); + throw new OpenGlException("Unable to make the context current"); + } + } + + public void Dispose() + { + if (!wglMakeCurrent(_oldDc, _oldContext)) + wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); + if (_monitor != null) + Monitor.Exit(_monitor); + } + } +} diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index fbc56e7703..523a059e0e 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -1,27 +1,29 @@ using Avalonia.OpenGL; using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Egl; +using Avalonia.Win32.OpenGl; namespace Avalonia.Win32 { static class Win32GlManager { - /// This property is initialized if drawing platform requests OpenGL support - public static EglPlatformOpenGlInterface EglPlatformInterface { get; private set; } - private static bool s_attemptedToInitialize; public static void Initialize() { - AvaloniaLocator.CurrentMutable.Bind().ToFunc(() => + AvaloniaLocator.CurrentMutable.Bind().ToLazy(() => { - if (!s_attemptedToInitialize) + var opts = AvaloniaLocator.Current.GetService(); + if (opts?.UseWgl == true) { - EglPlatformInterface = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); - s_attemptedToInitialize = true; + var wgl = WglPlatformOpenGlInterface.TryCreate(); + return wgl; } + + if (opts?.AllowEglInitialization == true) + return EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); - return EglPlatformInterface; + return null; }); } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index af6058d197..61c1a4f45e 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -11,6 +11,7 @@ using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; @@ -39,6 +40,12 @@ namespace Avalonia public bool AllowEglInitialization { get; set; } = true; public bool? EnableMultitouch { get; set; } public bool OverlayPopups { get; set; } + public bool UseWgl { get; set; } + public IList WglProfiles { get; set; } = new List + { + new GlVersion(GlProfileType.OpenGL, 4, 0), + new GlVersion(GlProfileType.OpenGL, 3, 2), + }; } } @@ -96,8 +103,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(new NonPumpingWaitProvider()) .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()); - if (options.AllowEglInitialization) - Win32GlManager.Initialize(); + Win32GlManager.Initialize(); _uiThread = Thread.CurrentThread; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 25a34561fc..655dfa5c48 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -338,19 +338,14 @@ namespace Avalonia.Win32 } case WindowsMessage.WM_PAINT: - { + { using (_rendererLock.Lock()) { - if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) - { - var f = RenderScaling; - var r = ps.rcPaint; - Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, - (r.bottom - r.top) / f)); - EndPaint(_hwnd, ref ps); - } + Paint?.Invoke(default); } + ValidateRect(hWnd, IntPtr.Zero); + return IntPtr.Zero; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index cb85e14e5a..7079a0120c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -13,6 +13,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; +using Avalonia.Win32.OpenGl; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 @@ -105,8 +106,12 @@ namespace Avalonia.Win32 CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); - if (Win32GlManager.EglPlatformInterface != null) - _gl = new EglGlPlatformSurface(Win32GlManager.EglPlatformInterface, this); + var glPlatform = AvaloniaLocator.Current.GetService(); + + if(glPlatform is EglPlatformOpenGlInterface egl) + _gl = new EglGlPlatformSurface(egl, this); + else if (glPlatform is WglPlatformOpenGlInterface wgl) + _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this); Screen = new ScreenImpl();