using OpenTK; using OpenTK.Graphics.OpenGL4; using OpenTK.Graphics.Wgl; using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Media; using WpfOpenGLSubwindowingClasses.Interop; using WpfOpenGLSubwindowingClasses.Renderer; using Wgl = WpfOpenGLSubwindowingClasses.Interop.Wgl; namespace WpfOpenGLSubwindowingClasses.Controls { public class OpenGlHwndHost : HwndHost { private IntPtr _hwnd; private IntPtr _hdc; private IntPtr _hglrc; private bool _initializedBindings; private double _dpiX = 1.0, _dpiY = 1.0; private static IntPtr s_sharedMasterCtx = IntPtr.Zero; public OpenGlHwndHost() { } // for xaml public OpenGLInstance Instance { get => (OpenGLInstance)GetValue(InstanceProperty); set => SetValue(InstanceProperty, value); } public static readonly DependencyProperty InstanceProperty = DependencyProperty.Register(nameof(Instance), typeof(OpenGLInstance), typeof(OpenGlHwndHost), new PropertyMetadata(null, OnInstanceChanged)); public bool ShareResourcesWithFirst { get => (bool)GetValue(ShareResourcesWithFirstProperty); set => SetValue(ShareResourcesWithFirstProperty, value); } public static readonly DependencyProperty ShareResourcesWithFirstProperty = DependencyProperty.Register(nameof(ShareResourcesWithFirst), typeof(bool), typeof(OpenGlHwndHost), new PropertyMetadata(true)); public bool DebugContext { get; set; } = false; const int WS_CHILD = 0x40000000; const int WS_VISIBLE = 0x10000000; public OpenGlHwndHost(OpenGLInstance instance) : this() { Instance = instance ?? throw new ArgumentNullException(nameof(instance)); } protected override HandleRef BuildWindowCore(HandleRef hwndParent) { var dpi = VisualTreeHelper.GetDpi(Application.Current.MainWindow); _dpiX = dpi.DpiScaleX; _dpiY = dpi.DpiScaleY; _hwnd = Wgl.CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0, 0, 1, 1, hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); _hdc = Wgl.GetDC(_hwnd); if (_hdc == IntPtr.Zero) throw new InvalidOperationException("GetDC failed."); if (!Wgl.ApplyDefaultPixelFormat(_hdc)) throw new InvalidOperationException("SetPixelFormat failed."); IntPtr share = (ShareResourcesWithFirst && s_sharedMasterCtx != IntPtr.Zero) ? s_sharedMasterCtx : IntPtr.Zero; _hglrc = Wgl.CreateBestContext(_hdc, shareWith: share, debug: DebugContext, coreProfile: true); if (_hglrc == IntPtr.Zero) throw new InvalidOperationException("wglCreateContext/CreateBestContext failed."); if (s_sharedMasterCtx == IntPtr.Zero && ShareResourcesWithFirst) s_sharedMasterCtx = _hglrc; Wgl.wglMakeCurrent(_hdc, _hglrc); if (!_initializedBindings) { GL.LoadBindings(new WglBindingsContext()); _initializedBindings = true; } var size = GetPixelSize(); InstanceResize(size.width, size.height); CompositionTarget.Rendering += OnRendering; return new HandleRef(this, _hwnd); } protected override void DestroyWindowCore(HandleRef hwnd) { CompositionTarget.Rendering -= OnRendering; Wgl.wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); if (_hglrc != IntPtr.Zero) { Wgl.wglDeleteContext(_hglrc); _hglrc = IntPtr.Zero; } if (_hdc != IntPtr.Zero) { Wgl.ReleaseDC(_hwnd, _hdc); _hdc = IntPtr.Zero; } if (hwnd.Handle != IntPtr.Zero) { Wgl.DestroyWindow(hwnd.Handle); } try { Instance?.Dispose(); } catch { } } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); var size = GetPixelSize(); InstanceResize(size.width, size.height); } private void OnRendering(object sender, EventArgs e) { if (_hdc == IntPtr.Zero || _hglrc == IntPtr.Zero) return; Wgl.wglMakeCurrent(_hdc, _hglrc); Instance.Render(); Wgl.SwapBuffers(_hdc); Wgl.wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); } private (int width, int height) GetPixelSize() { int w = (int)Math.Max(1, Math.Round(ActualWidth * _dpiX)); int h = (int)Math.Max(1, Math.Round(ActualHeight * _dpiY)); return (w, h); } private void InstanceResize(int pixelWidth, int pixelHeight) { try { Instance.EnqueueOnGlThread(() => { Instance.Resize(pixelWidth, pixelHeight); }); } catch { /* ignore */ } } private static void OnInstanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var host = (OpenGlHwndHost)d; if (host._hwnd != IntPtr.Zero && host.Instance != null) { var size = host.GetPixelSize(); host.InstanceResize(size.width, size.height); } } public IntPtr Hwnd => _hwnd; public IntPtr Hglrc => _hglrc; } }