VeldridView.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. using Newtonsoft.Json.Linq;
  2. using System;
  3. using System.Diagnostics;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Numerics;
  6. using System.Reflection;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Data;
  13. using System.Windows.Documents;
  14. using System.Windows.Input;
  15. using System.Windows.Interop;
  16. using System.Windows.Media;
  17. using System.Windows.Media.Imaging;
  18. using System.Windows.Media.Media3D;
  19. using System.Windows.Navigation;
  20. using System.Windows.Shapes;
  21. namespace Veldrid.Wpf
  22. {
  23. public sealed class VeldridContent
  24. {
  25. static VeldridContent() { }
  26. private VeldridContent() { }
  27. private const string WindowClass = "HwndWrapper";
  28. public IntPtr Hwnd { get; private set; }
  29. [AllowNull]
  30. public Veldrid.Common.VeldridContent Content { get; private set; }
  31. public static Veldrid.Wpf.VeldridContent CreateContent()
  32. {
  33. VeldridContent content = new VeldridContent();
  34. var wndClass = new NativeMethods.WndClassEx();
  35. wndClass.cbSize = (uint)Marshal.SizeOf(wndClass);
  36. wndClass.hInstance = NativeMethods.GetModuleHandle(null);
  37. wndClass.lpfnWndProc = NativeMethods.DefaultWindowProc;
  38. wndClass.lpszClassName = WindowClass;
  39. wndClass.hCursor = NativeMethods.LoadCursor(IntPtr.Zero, NativeMethods.IDC_ARROW);
  40. NativeMethods.RegisterClassEx(ref wndClass);
  41. content.Hwnd = NativeMethods.CreateWindowEx(
  42. 0, WindowClass, "", NativeMethods.WS_VISIBLE,
  43. 0, 0, 200, 200, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0);
  44. NativeMethods.ShowWindow(content.Hwnd, 0);
  45. Module mainModule = typeof(VeldridView).Module;
  46. IntPtr hinstance = Marshal.GetHINSTANCE(mainModule);
  47. SwapchainSource win32Source = SwapchainSource.CreateWin32(content.Hwnd, hinstance);
  48. SwapchainDescription scDesc = new SwapchainDescription(win32Source, 200, 200, PixelFormat.R32_Float, true);
  49. GraphicsDevice device = GraphicsDevice.CreateD3D11(new GraphicsDeviceOptions(false, null, true, ResourceBindingModel.Default, true, true, true), scDesc);
  50. content.Content = new Common.VeldridContent(device);
  51. return content;
  52. }
  53. }
  54. public sealed class VeldridView : HwndHost
  55. {
  56. [AllowNull]
  57. private VeldridContent _Content;
  58. private IntPtr Hwnd;
  59. public bool HwndInitialized { get; private set; }
  60. public VeldridView(VeldridContent content)
  61. {
  62. _Content = content;
  63. Hwnd = _Content.Hwnd;
  64. base.Loaded += OnLoaded;
  65. base.Unloaded += OnUnloaded;
  66. }
  67. public GraphicsBackend Backend { get; set; } = GraphicsBackend.Direct3D11;
  68. private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
  69. {
  70. Initialize();
  71. HwndInitialized = true;
  72. CreateGraphicsDevice();
  73. }
  74. private void CreateGraphicsDevice()
  75. {
  76. double dpiScale = CompositionScaleX;
  77. uint width = (uint)(ActualWidth < 0 ? 0 : Math.Ceiling(ActualWidth * dpiScale));
  78. uint height = (uint)(ActualHeight < 0 ? 0 : Math.Ceiling(ActualHeight * dpiScale));
  79. width = (uint)ActualWidth;
  80. height = (uint)ActualHeight;
  81. NativeMethods.SetWindowLong(Hwnd, -16, 0x40000000L);
  82. //NativeMethods.SetWindowLong(Hwnd, -20, 0x08000000);
  83. NativeMethods.SetParent(_Content.Hwnd, parentptr);
  84. NativeMethods.ShowWindow(Hwnd, 1);
  85. NativeMethods.SetWindowPos(Hwnd, 0, 0, 0, (int)width, (int)height, 0);
  86. if (ActualWidth > 0 && ActualHeight > 0)
  87. {
  88. _Content.Content.Resize(width,height);
  89. }
  90. GraphicsDeviceCreated?.Invoke();
  91. Thread.Sleep(20);
  92. }
  93. private void DestroyGraphicsDevice()
  94. {
  95. if (_Content != null)
  96. {
  97. GraphicsDeviceDestroyed?.Invoke();
  98. _Content = null;
  99. }
  100. }
  101. [AllowNull]
  102. public Action GraphicsDeviceDestroyed;
  103. [AllowNull]
  104. public Action GraphicsDeviceCreated;
  105. private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
  106. {
  107. Uninitialize();
  108. HwndInitialized = false;
  109. DestroyGraphicsDevice();
  110. Dispose();
  111. }
  112. public IntPtr NativeHwnd => this.Hwnd;
  113. [AllowNull]
  114. public new Action SizeChanged;
  115. [AllowNull]
  116. public new Action Loaded;
  117. [AllowNull]
  118. public new Action Unloaded;
  119. private void Initialize()
  120. {
  121. Loaded?.Invoke();
  122. }
  123. private void Resized()
  124. {
  125. if (_Content != null)
  126. {
  127. if (ActualWidth > 0 && ActualHeight > 0)
  128. {
  129. _Content.Content.Resize((uint)(ActualWidth * CompositionScaleX), (uint)(ActualHeight * CompositionScaleY));
  130. }
  131. }
  132. SizeChanged?.Invoke();
  133. }
  134. public double CompositionScaleX => GetDpiScale();
  135. public double CompositionScaleY => GetDpiScale();
  136. private double GetDpiScale()
  137. {
  138. PresentationSource source = PresentationSource.FromVisual(this);
  139. if (source != null)
  140. return source.CompositionTarget.TransformToDevice.M11;
  141. else
  142. return 1;
  143. }
  144. private void Uninitialize()
  145. {
  146. Unloaded?.Invoke();
  147. }
  148. protected override HandleRef BuildWindowCore(HandleRef hwndParent)
  149. {
  150. parentptr = hwndParent.Handle;
  151. NativeMethods.SetWindowLong(Hwnd, -16, 0x40000000L);
  152. //NativeMethods.SetWindowLong(Hwnd, -20, 0x80000000L);
  153. NativeMethods.SetParent(_Content.Hwnd, parentptr);
  154. if (ActualWidth > 0 && ActualHeight > 0)
  155. {
  156. _Content.Content.Resize((uint)(ActualWidth*CompositionScaleX), (uint)(ActualHeight*CompositionScaleY));
  157. }
  158. NativeMethods.ShowWindow(Hwnd, 1);
  159. return new HandleRef(this, Hwnd);
  160. }
  161. private IntPtr parentptr;
  162. protected override void DestroyWindowCore(HandleRef hwnd)
  163. {
  164. NativeMethods.ShowWindow(Hwnd, 0);
  165. var result = NativeMethods.GetWindowLong(Hwnd, -16);
  166. result &= ~0x40000000L;
  167. NativeMethods.SetWindowLong(Hwnd, -16, result);
  168. NativeMethods.SetParent(Hwnd, IntPtr.Zero);
  169. Hwnd = IntPtr.Zero;
  170. _Content = null;
  171. }
  172. protected unsafe override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
  173. {
  174. if (_Content != null)
  175. {
  176. tagRECT rect = new tagRECT();
  177. GetWindowRect(hwnd, ref rect);
  178. switch (msg)
  179. {
  180. case NativeMethods.WM_LBUTTONDOWN:
  181. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  182. int val = (int)lParam.ToInt64();
  183. _Content.Content.MouseLeftDown(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val));
  184. break;
  185. case NativeMethods.WM_LBUTTONUP:
  186. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  187. val = (int)lParam.ToInt64();
  188. _Content.Content.MouseLeftUp(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val));
  189. break;
  190. case NativeMethods.WM_RBUTTONDOWN:
  191. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  192. val = (int)lParam.ToInt64();
  193. _Content.Content.MouseRightDown(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val));
  194. break;
  195. case NativeMethods.WM_RBUTTONUP:
  196. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  197. val = (int)lParam.ToInt64();
  198. _Content.Content.MouseRightUp(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val));
  199. break;
  200. case NativeMethods.WM_MBUTTONDOWN:
  201. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  202. val = (int)lParam.ToInt64();
  203. _Content.Content.MouseMiddleDown(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val) );
  204. break;
  205. case NativeMethods.WM_MBUTTONUP:
  206. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  207. val = (int)lParam.ToInt64();
  208. _Content.Content.MouseMiddleUp(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val) );
  209. break;
  210. case NativeMethods.WM_MOUSEMOVE:
  211. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  212. val = (int)lParam.ToInt64();
  213. _Content.Content.MouseMove(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val));
  214. break;
  215. case NativeMethods.WM_MOUSEWHEEL:
  216. _Content.Content.WindowScale = new Vector2((float)ActualWidth / (rect.right - rect.left), (float)ActualHeight / (rect.bottom - rect.top));
  217. val = (int)lParam.ToInt64();
  218. _Content.Content.MouseWheel(Unsafe.As<int, Veldrid.Common.MousePositon>(ref val) , (((Int32)wParam.ToInt64()) >> 16) / Mouse.MouseWheelDeltaForOneLine);
  219. break;
  220. case NativeMethods.WM_MOUSELEAVE:
  221. _Content.Content.MouseLeave();
  222. break;
  223. }
  224. }
  225. return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
  226. }
  227. [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
  228. private struct tagRECT
  229. {
  230. /// LONG->int
  231. public int left;
  232. /// LONG->int
  233. public int top;
  234. /// LONG->int
  235. public int right;
  236. /// LONG->int
  237. public int bottom;
  238. }
  239. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "GetWindowRect")]
  240. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  241. private static extern bool GetWindowRect(System.IntPtr hWnd, ref tagRECT lpRect);
  242. [StructLayout(LayoutKind.Sequential)]
  243. private struct MSLLHOOKSTRUCT
  244. {
  245. public Point pt;
  246. public int mouseData;
  247. public int flags;
  248. public int time;
  249. public long dwExtraInfo;
  250. }
  251. private void RaiseMouseEvent(System.Windows.Input.MouseButton button, RoutedEvent @event)
  252. {
  253. RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)DateTime.Now.Ticks, button)
  254. {
  255. RoutedEvent = @event,
  256. Source = this,
  257. });
  258. }
  259. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  260. {
  261. UpdateWindowPos();
  262. base.OnRenderSizeChanged(sizeInfo);
  263. if (HwndInitialized)
  264. Resized();
  265. }
  266. }
  267. internal class NativeMethods
  268. {
  269. // ReSharper disable InconsistentNaming
  270. public const int WS_CHILD = 0x40000000;
  271. public const int WS_VISIBLE = 0x10000000;
  272. public const int WM_PAINT = 0x0f;
  273. public const int WM_LBUTTONDOWN = 0x0201;
  274. public const int WM_LBUTTONUP = 0x0202;
  275. public const int WM_RBUTTONDOWN = 0x0204;
  276. public const int WM_RBUTTONUP = 0x0205;
  277. public const int WM_NCCALCSIZE = 0x0083;
  278. public const int WM_MOUSEWHEEL = 0x020A;
  279. public const int WM_MBUTTONDOWN = 0x0207;
  280. public const int WM_MBUTTONUP = 0x0208;
  281. public const int WM_MOUSEMOVE = 0x0200;
  282. public const int WM_MOUSELEAVE = 0x02A3;
  283. public const int IDC_ARROW = 32512;
  284. [StructLayout(LayoutKind.Sequential)]
  285. public struct WndClassEx
  286. {
  287. public uint cbSize;
  288. public uint style;
  289. [MarshalAs(UnmanagedType.FunctionPtr)]
  290. public WndProc lpfnWndProc;
  291. public int cbClsExtra;
  292. public int cbWndExtra;
  293. public IntPtr hInstance;
  294. public IntPtr hIcon;
  295. public IntPtr hCursor;
  296. public IntPtr hbrBackground;
  297. public string lpszMenuName;
  298. public string lpszClassName;
  299. public IntPtr hIconSm;
  300. }
  301. [DllImport("user32.dll")]
  302. public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
  303. public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
  304. public static readonly WndProc DefaultWindowProc = DefWindowProc;
  305. [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Auto)]
  306. public static extern IntPtr CreateWindowEx(
  307. int exStyle,
  308. string className,
  309. string windowName,
  310. int style,
  311. int x, int y,
  312. int width, int height,
  313. IntPtr hwndParent,
  314. IntPtr hMenu,
  315. IntPtr hInstance,
  316. [MarshalAs(UnmanagedType.AsAny)] object pvParam);
  317. [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Auto)]
  318. public static extern bool DestroyWindow(IntPtr hwnd);
  319. [DllImport("kernel32.dll")]
  320. public static extern IntPtr GetModuleHandle(string module);
  321. [DllImport("user32.dll")]
  322. [return: MarshalAs(UnmanagedType.U2)]
  323. public static extern short RegisterClassEx([In] ref WndClassEx lpwcx);
  324. [DllImport("user32.dll")]
  325. public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
  326. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetParent")]
  327. public static extern System.IntPtr SetParent(System.IntPtr hWndChild, System.IntPtr hWndNewParent);
  328. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "ShowWindow")]
  329. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  330. public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow);
  331. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetWindowPos")]
  332. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  333. public static extern bool SetWindowPos(System.IntPtr hWnd, System.IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
  334. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetWindowLong")]
  335. public static extern long SetWindowLong(System.IntPtr hWnd, int nIndex, long dwNewLong);
  336. [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "GetWindowLong")]
  337. public static extern long GetWindowLong(System.IntPtr hWnd, int nIndex);
  338. }
  339. }