PageRenderTime 59ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/GoliatEditor/OpenTK/1.1/Source/OpenTK/Platform/Windows/WinGLContext.cs

https://gitlab.com/gonzague.defraiteur/GoliatEditor
C# | 509 lines | 376 code | 82 blank | 51 comment | 67 complexity | 2aa6b5bd4dc08fad9e6180b155ea72a9 MD5 | raw file
  1. #region --- License ---
  2. /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
  3. * Contributions from Erik Ylvisaker
  4. * See license.txt for license info
  5. */
  6. #endregion
  7. #region --- Using Directives ---
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using System.Runtime.InteropServices;
  12. using System.Diagnostics;
  13. using OpenTK.Graphics;
  14. using OpenTK.Graphics.OpenGL;
  15. #endregion
  16. namespace OpenTK.Platform.Windows
  17. {
  18. /// \internal
  19. /// <summary>
  20. /// Provides methods to create and control an opengl context on the Windows platform.
  21. /// This class supports OpenTK, and is not intended for use by OpenTK programs.
  22. /// </summary>
  23. internal sealed class WinGLContext : DesktopGraphicsContext
  24. {
  25. static readonly object LoadLock = new object();
  26. IntPtr device_context;
  27. bool vsync_supported;
  28. bool vsync_tear_supported;
  29. readonly WinGraphicsMode ModeSelector;
  30. // We need to create a temp context in order to load
  31. // wgl extensions (e.g. for multisampling or GL3).
  32. // We cannot rely on any WGL extensions before
  33. // we load them with the temporary context.
  34. class TemporaryContext : IDisposable
  35. {
  36. public ContextHandle Context;
  37. public TemporaryContext(INativeWindow native)
  38. {
  39. Debug.WriteLine("[WGL] Creating temporary context to load extensions");
  40. if (native == null)
  41. throw new ArgumentNullException();
  42. // Create temporary context and load WGL entry points
  43. // First, set a compatible pixel format to the device context
  44. // of the temp window
  45. WinWindowInfo window = native.WindowInfo as WinWindowInfo;
  46. WinGraphicsMode selector = new WinGraphicsMode(window.DeviceContext);
  47. WinGLContext.SetGraphicsModePFD(selector, GraphicsMode.Default, window);
  48. bool success = false;
  49. // Then, construct a temporary context and load all wgl extensions
  50. Context = new ContextHandle(Wgl.CreateContext(window.DeviceContext));
  51. if (Context != ContextHandle.Zero)
  52. {
  53. // Make the context current.
  54. // Note: on some video cards and on some virtual machines, wglMakeCurrent
  55. // may fail with an errorcode of 6 (INVALID_HANDLE). The suggested workaround
  56. // is to call wglMakeCurrent in a loop until it succeeds.
  57. // See https://www.opengl.org/discussion_boards/showthread.php/171058-nVidia-wglMakeCurrent()-multiple-threads
  58. // Sigh...
  59. for (int retry = 0; retry < 5 && !success; retry++)
  60. {
  61. success = Wgl.MakeCurrent(window.DeviceContext, Context.Handle);
  62. if (!success)
  63. {
  64. Debug.Print("wglMakeCurrent failed with error: {0}. Retrying", Marshal.GetLastWin32Error());
  65. System.Threading.Thread.Sleep(10);
  66. }
  67. }
  68. }
  69. else
  70. {
  71. Debug.Print("[WGL] CreateContext failed with error: {0}", Marshal.GetLastWin32Error());
  72. }
  73. if (!success)
  74. {
  75. Debug.WriteLine("[WGL] Failed to create temporary context");
  76. }
  77. }
  78. public void Dispose()
  79. {
  80. if (Context != ContextHandle.Zero)
  81. {
  82. Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
  83. Wgl.DeleteContext(Context.Handle);
  84. }
  85. }
  86. }
  87. #region --- Contructors ---
  88. public WinGLContext(GraphicsMode format, WinWindowInfo window, IGraphicsContext sharedContext,
  89. int major, int minor, GraphicsContextFlags flags)
  90. {
  91. // There are many ways this code can break when accessed by multiple threads. The biggest offender is
  92. // the sharedContext stuff, which will only become valid *after* this constructor returns.
  93. // The easiest solution is to serialize all context construction - hence the big lock, below.
  94. lock (LoadLock)
  95. {
  96. if (window == null)
  97. throw new ArgumentNullException("window", "Must point to a valid window.");
  98. if (window.Handle == IntPtr.Zero)
  99. throw new ArgumentException("window", "Must be a valid window.");
  100. IntPtr current_context = Wgl.GetCurrentContext();
  101. INativeWindow temp_window = null;
  102. TemporaryContext temp_context = null;
  103. try
  104. {
  105. if (current_context == IntPtr.Zero)
  106. {
  107. // Create temporary context to load WGL extensions
  108. temp_window = new NativeWindow();
  109. temp_context = new TemporaryContext(temp_window);
  110. current_context = Wgl.GetCurrentContext();
  111. if (current_context != IntPtr.Zero && current_context == temp_context.Context.Handle)
  112. {
  113. new Wgl().LoadEntryPoints();
  114. }
  115. }
  116. Debug.Print("OpenGL will be bound to window:{0} on thread:{1}", window.Handle,
  117. System.Threading.Thread.CurrentThread.ManagedThreadId);
  118. ModeSelector = new WinGraphicsMode(window.DeviceContext);
  119. Mode = SetGraphicsModePFD(ModeSelector, format, (WinWindowInfo)window);
  120. if (Wgl.SupportsFunction("wglCreateContextAttribsARB"))
  121. {
  122. try
  123. {
  124. Debug.Write("Using WGL_ARB_create_context... ");
  125. List<int> attributes = new List<int>();
  126. attributes.Add((int)ArbCreateContext.MajorVersion);
  127. attributes.Add(major);
  128. attributes.Add((int)ArbCreateContext.MinorVersion);
  129. attributes.Add(minor);
  130. if (flags != 0)
  131. {
  132. attributes.Add((int)ArbCreateContext.ContextFlags);
  133. attributes.Add((int)GetARBContextFlags(flags));
  134. attributes.Add((int)ArbCreateContext.ProfileMask);
  135. attributes.Add((int)GetARBContextProfile(flags));
  136. }
  137. // According to the docs, " <attribList> specifies a list of attributes for the context.
  138. // The list consists of a sequence of <name,value> pairs terminated by the
  139. // value 0. [...]"
  140. // Is this a single 0, or a <0, 0> pair? (Defensive coding: add two zeroes just in case).
  141. attributes.Add(0);
  142. attributes.Add(0);
  143. Handle = new ContextHandle(
  144. Wgl.Arb.CreateContextAttribs(
  145. window.DeviceContext,
  146. sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero,
  147. attributes.ToArray()));
  148. if (Handle == ContextHandle.Zero)
  149. Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error());
  150. }
  151. catch (Exception e) { Debug.Print(e.ToString()); }
  152. }
  153. if (Handle == ContextHandle.Zero)
  154. {
  155. // Failed to create GL3-level context, fall back to GL2.
  156. Debug.Write("Falling back to GL2... ");
  157. Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext));
  158. if (Handle == ContextHandle.Zero)
  159. Handle = new ContextHandle(Wgl.CreateContext(window.DeviceContext));
  160. if (Handle == ContextHandle.Zero)
  161. throw new GraphicsContextException(
  162. String.Format("Context creation failed. Wgl.CreateContext() error: {0}.",
  163. Marshal.GetLastWin32Error()));
  164. }
  165. Debug.WriteLine(String.Format("success! (id: {0})", Handle));
  166. }
  167. finally
  168. {
  169. if (temp_context != null)
  170. {
  171. temp_context.Dispose();
  172. temp_context = null;
  173. }
  174. if (temp_window != null)
  175. {
  176. temp_window.Dispose();
  177. temp_window = null;
  178. }
  179. }
  180. }
  181. // Todo: is this comment still true?
  182. // On intel drivers, wgl entry points appear to change
  183. // when creating multiple contexts. As a workaround,
  184. // we reload Wgl entry points every time we create a
  185. // new context - this solves the issue without any apparent
  186. // side-effects (i.e. the old contexts can still be handled
  187. // using the new entry points.)
  188. // Sigh...
  189. MakeCurrent(window);
  190. new Wgl().LoadEntryPoints();
  191. if (sharedContext != null)
  192. {
  193. Marshal.GetLastWin32Error();
  194. Debug.Write(String.Format("Sharing state with context {0}: ", sharedContext));
  195. bool result = Wgl.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle);
  196. Debug.WriteLine(result ? "success!" : "failed with win32 error " + Marshal.GetLastWin32Error());
  197. }
  198. }
  199. static ArbCreateContext GetARBContextFlags(GraphicsContextFlags flags)
  200. {
  201. ArbCreateContext result = 0;
  202. result |= (flags & GraphicsContextFlags.ForwardCompatible) != 0 ?
  203. ArbCreateContext.CoreProfileBit : ArbCreateContext.CompatibilityProfileBit;
  204. return result;
  205. }
  206. static ArbCreateContext GetARBContextProfile(GraphicsContextFlags flags)
  207. {
  208. ArbCreateContext result = 0;
  209. result |= (flags & GraphicsContextFlags.Debug) != 0 ? ArbCreateContext.DebugBit : 0;
  210. return result;
  211. }
  212. public WinGLContext(ContextHandle handle, WinWindowInfo window, IGraphicsContext sharedContext,
  213. int major, int minor, GraphicsContextFlags flags)
  214. {
  215. if (handle == ContextHandle.Zero)
  216. throw new ArgumentException("handle");
  217. if (window == null)
  218. throw new ArgumentNullException("window");
  219. Handle = handle;
  220. }
  221. #endregion
  222. #region IGraphicsContext Members
  223. #region SwapBuffers
  224. public override void SwapBuffers()
  225. {
  226. if (!Functions.SwapBuffers(DeviceContext))
  227. throw new GraphicsContextException(String.Format(
  228. "Failed to swap buffers for context {0} current. Error: {1}", this, Marshal.GetLastWin32Error()));
  229. }
  230. #endregion
  231. #region MakeCurrent
  232. public override void MakeCurrent(IWindowInfo window)
  233. {
  234. lock (LoadLock)
  235. {
  236. bool success;
  237. WinWindowInfo wnd = window as WinWindowInfo;
  238. if (wnd != null)
  239. {
  240. if (wnd.Handle == IntPtr.Zero)
  241. throw new ArgumentException("window", "Must point to a valid window.");
  242. success = Wgl.MakeCurrent(wnd.DeviceContext, Handle.Handle);
  243. device_context = wnd.DeviceContext;
  244. }
  245. else
  246. {
  247. success = Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
  248. device_context = IntPtr.Zero;
  249. }
  250. if (!success)
  251. {
  252. throw new GraphicsContextException(String.Format(
  253. "Failed to make context {0} current. Error: {1}", this, Marshal.GetLastWin32Error()));
  254. }
  255. }
  256. }
  257. #endregion
  258. #region IsCurrent
  259. public override bool IsCurrent
  260. {
  261. get { return Wgl.GetCurrentContext() == Handle.Handle; }
  262. }
  263. #endregion
  264. #region SwapInterval
  265. public override int SwapInterval
  266. {
  267. get
  268. {
  269. lock (LoadLock)
  270. {
  271. if (vsync_supported)
  272. return Wgl.Ext.GetSwapInterval();
  273. else
  274. return 0;
  275. }
  276. }
  277. set
  278. {
  279. lock (LoadLock)
  280. {
  281. if (vsync_supported)
  282. {
  283. if (value < 0 && !vsync_tear_supported)
  284. {
  285. value = 1;
  286. }
  287. if (!Wgl.Ext.SwapInterval(value))
  288. {
  289. Debug.Print("wglSwapIntervalEXT call failed.");
  290. }
  291. }
  292. }
  293. }
  294. }
  295. #endregion
  296. #region void LoadAll()
  297. public override void LoadAll()
  298. {
  299. lock (LoadLock)
  300. {
  301. new Wgl().LoadEntryPoints();
  302. vsync_supported =
  303. Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_control") &&
  304. Wgl.SupportsFunction("wglGetSwapIntervalEXT") &&
  305. Wgl.SupportsFunction("wglSwapIntervalEXT");
  306. vsync_tear_supported =
  307. Wgl.SupportsExtension(DeviceContext, "WGL_EXT_swap_control_tear");
  308. }
  309. base.LoadAll();
  310. }
  311. #endregion
  312. #endregion
  313. #region IGLContextInternal Members
  314. #region IWindowInfo IGLContextInternal.Info
  315. /*
  316. IWindowInfo IGraphicsContextInternal.Info
  317. {
  318. get { return (IWindowInfo)windowInfo; }
  319. }
  320. */
  321. #endregion
  322. #region GetAddress
  323. public override IntPtr GetAddress(IntPtr function_string)
  324. {
  325. IntPtr address = Wgl.GetProcAddress(function_string);
  326. if (!IsValid(address))
  327. {
  328. address = Functions.GetProcAddress(WinFactory.OpenGLHandle, function_string);
  329. }
  330. return address;
  331. }
  332. static bool IsValid(IntPtr address)
  333. {
  334. // See https://www.opengl.org/wiki/Load_OpenGL_Functions
  335. long a = address.ToInt64();
  336. bool is_valid = (a < -1) || (a > 3);
  337. return is_valid;
  338. }
  339. #endregion
  340. #endregion
  341. #region Internal Methods
  342. #region SetGraphicsModePFD
  343. // Note: there is no relevant ARB function.
  344. internal static GraphicsMode SetGraphicsModePFD(WinGraphicsMode mode_selector,
  345. GraphicsMode mode, WinWindowInfo window)
  346. {
  347. Debug.Write("Setting pixel format... ");
  348. if (window == null)
  349. throw new ArgumentNullException("window", "Must point to a valid window.");
  350. if (!mode.Index.HasValue)
  351. {
  352. mode = mode_selector.SelectGraphicsMode(
  353. mode.ColorFormat, mode.Depth, mode.Stencil,
  354. mode.Samples, mode.AccumulatorFormat,
  355. mode.Buffers, mode.Stereo);
  356. }
  357. PixelFormatDescriptor pfd = new PixelFormatDescriptor();
  358. Functions.DescribePixelFormat(
  359. window.DeviceContext, (int)mode.Index.Value,
  360. API.PixelFormatDescriptorSize, ref pfd);
  361. Debug.WriteLine(mode.Index.ToString());
  362. if (!Functions.SetPixelFormat(window.DeviceContext, (int)mode.Index.Value, ref pfd))
  363. {
  364. throw new GraphicsContextException(String.Format(
  365. "Requested GraphicsMode not available. SetPixelFormat error: {0}",
  366. Marshal.GetLastWin32Error()));
  367. }
  368. return mode;
  369. }
  370. #endregion
  371. #region internal IntPtr DeviceContext
  372. internal IntPtr DeviceContext
  373. {
  374. get
  375. {
  376. return device_context;
  377. }
  378. }
  379. #endregion
  380. #endregion
  381. #region Overrides
  382. /// <summary>Returns a System.String describing this OpenGL context.</summary>
  383. /// <returns>A System.String describing this OpenGL context.</returns>
  384. public override string ToString()
  385. {
  386. return (this as IGraphicsContextInternal).Context.ToString();
  387. }
  388. #endregion
  389. #region IDisposable Members
  390. protected override void Dispose(bool calledManually)
  391. {
  392. if (!IsDisposed)
  393. {
  394. if (calledManually)
  395. {
  396. DestroyContext();
  397. }
  398. IsDisposed = true;
  399. }
  400. }
  401. #region private void DestroyContext()
  402. private void DestroyContext()
  403. {
  404. if (Handle != ContextHandle.Zero)
  405. {
  406. try
  407. {
  408. // This will fail if the user calls Dispose() on thread X when the context is current on thread Y.
  409. if (!Wgl.DeleteContext(Handle.Handle))
  410. Debug.Print("Failed to destroy OpenGL context {0}. Error: {1}",
  411. Handle.ToString(), Marshal.GetLastWin32Error());
  412. }
  413. catch (AccessViolationException e)
  414. {
  415. Debug.WriteLine("An access violation occured while destroying the OpenGL context. Please report at http://www.opentk.com.");
  416. Debug.Indent();
  417. Debug.Print("Marshal.GetLastWin32Error(): {0}", Marshal.GetLastWin32Error().ToString());
  418. Debug.WriteLine(e.ToString());
  419. Debug.Unindent();
  420. }
  421. Handle = ContextHandle.Zero;
  422. }
  423. }
  424. #endregion
  425. #endregion
  426. }
  427. }