Files
WpfOpenGLSubwindowing/WpfOpenGLSubwindowingClasses/WpfOpenGLSubwindowingClasses/OpenGLHwndHost.cs
T
2026-01-09 15:16:22 +01:00

179 lines
5.8 KiB
C#

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