179 lines
5.8 KiB
C#
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;
|
|
}
|
|
|
|
} |