From 544cf9d405532f08fb97749171e9088837e420d8 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Tue, 3 Nov 2009 23:39:03 +0000 Subject: [PATCH] Serialize context construction to avoid threading issues with shared contexts. --- Source/OpenTK/Graphics/GraphicsContext.cs | 83 +++++----- .../OpenTK/Platform/Windows/WinGLContext.cs | 154 ++++++++++-------- 2 files changed, 123 insertions(+), 114 deletions(-) diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index 6aa5e9fd..c6ef8501 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -32,7 +32,7 @@ namespace OpenTK.Graphics static bool share_contexts = true; static bool direct_rendering = true; - readonly static object context_lock = new object(); + readonly static object SyncRoot = new object(); // Maps OS-specific context handles to GraphicsContext weak references. readonly static Dictionary available_contexts = new Dictionary(); @@ -50,7 +50,7 @@ namespace OpenTK.Graphics { implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle); - lock (context_lock) + lock (SyncRoot) { available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this)); } @@ -78,31 +78,31 @@ namespace OpenTK.Graphics /// public GraphicsContext(GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags) { - bool designMode = false; - if (mode == null && window == null) - designMode = true; - else if (mode == null) throw new ArgumentNullException("mode", "Must be a valid GraphicsMode."); - else if (window == null) throw new ArgumentNullException("window", "Must point to a valid window."); - - // Silently ignore invalid major and minor versions. - if (major <= 0) - major = 1; - if (minor < 0) - minor = 0; - - Debug.Print("Creating GraphicsContext."); - try + lock (SyncRoot) { - Debug.Indent(); - Debug.Print("GraphicsMode: {0}", mode); - Debug.Print("IWindowInfo: {0}", window); - Debug.Print("GraphicsContextFlags: {0}", flags); - Debug.Print("Requested version: {0}.{1}", major, minor); + bool designMode = false; + if (mode == null && window == null) + designMode = true; + else if (mode == null) throw new ArgumentNullException("mode", "Must be a valid GraphicsMode."); + else if (window == null) throw new ArgumentNullException("window", "Must point to a valid window."); - IGraphicsContext shareContext = null; - if (GraphicsContext.ShareContexts) + // Silently ignore invalid major and minor versions. + if (major <= 0) + major = 1; + if (minor < 0) + minor = 0; + + Debug.Print("Creating GraphicsContext."); + try { - lock (context_lock) + Debug.Indent(); + Debug.Print("GraphicsMode: {0}", mode); + Debug.Print("IWindowInfo: {0}", window); + Debug.Print("GraphicsContextFlags: {0}", flags); + Debug.Print("Requested version: {0}.{1}", major, minor); + + IGraphicsContext shareContext = null; + if (GraphicsContext.ShareContexts) { // A small hack to create a shared context with the first available context. foreach (WeakReference r in GraphicsContext.available_contexts.Values) @@ -111,26 +111,23 @@ namespace OpenTK.Graphics break; } } - } - // Todo: Add a DummyFactory implementing IPlatformFactory. - if (designMode) - implementation = new Platform.Dummy.DummyGLContext(); - else - switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded) - { - case false: implementation = Factory.Default.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags); break; - case true: implementation = Factory.Embedded.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags); break; - } + // Todo: Add a DummyFactory implementing IPlatformFactory. + if (designMode) + implementation = new Platform.Dummy.DummyGLContext(); + else + switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded) + { + case false: implementation = Factory.Default.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags); break; + case true: implementation = Factory.Embedded.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags); break; + } - lock (context_lock) - { available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this)); } - } - finally - { - Debug.Unindent(); + finally + { + Debug.Unindent(); + } } } @@ -201,7 +198,7 @@ namespace OpenTK.Graphics { get { - lock (context_lock) + lock (SyncRoot) { if (available_contexts.Count > 0) { @@ -282,7 +279,7 @@ namespace OpenTK.Graphics /// void CreateContext(bool direct, IGraphicsContext source) { - lock (context_lock) + lock (SyncRoot) { available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this)); } @@ -418,7 +415,7 @@ namespace OpenTK.Graphics if (!IsDisposed) { Debug.Print("Disposing context {0}.", (this as IGraphicsContextInternal).Context.ToString()); - lock (context_lock) + lock (SyncRoot) { available_contexts.Remove((this as IGraphicsContextInternal).Context); } diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 9933d653..9588f855 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -26,6 +26,8 @@ namespace OpenTK.Platform.Windows /// internal sealed class WinGLContext : DesktopGraphicsContext { + static object SyncRoot = new object(); + static IntPtr opengl32Handle; static bool wgl_loaded; const string opengl32Name = "OPENGL32.DLL"; @@ -36,96 +38,106 @@ namespace OpenTK.Platform.Windows static WinGLContext() { - // Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl. - if (opengl32Handle == IntPtr.Zero) + lock (SyncRoot) { - opengl32Handle = Functions.LoadLibrary(opengl32Name); + // Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl. if (opengl32Handle == IntPtr.Zero) - throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", - opengl32Name, Marshal.GetLastWin32Error())); - Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); + { + opengl32Handle = Functions.LoadLibrary(opengl32Name); + if (opengl32Handle == IntPtr.Zero) + throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", + opengl32Name, Marshal.GetLastWin32Error())); + Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); + } } } public WinGLContext(GraphicsMode format, WinWindowInfo window, IGraphicsContext sharedContext, int major, int minor, GraphicsContextFlags flags) { - if (window == null) - throw new ArgumentNullException("window", "Must point to a valid window."); - if (window.WindowHandle == IntPtr.Zero) - throw new ArgumentException("window", "Must be a valid window."); - - Mode = format; - - Debug.Print("OpenGL will be bound to handle: {0}", window.WindowHandle); - Debug.Write("Setting pixel format... "); - this.SetGraphicsModePFD(format, (WinWindowInfo)window); - - if (!wgl_loaded) + // There are many ways this code can break when accessed by multiple threads. The biggest offender is + // the sharedContext stuff, which will only become valid *after* this constructor returns. + // The easiest solution is to serialize all context construction - hence the big lock, below. + lock (SyncRoot) { - // We need to create a temp context in order to load wgl extensions (e.g. for multisampling or GL3). - // We cannot rely on OpenTK.Platform.Wgl until we create the context and call Wgl.LoadAll(). - Debug.Print("Creating temporary context for wgl extensions."); + if (window == null) + throw new ArgumentNullException("window", "Must point to a valid window."); + if (window.WindowHandle == IntPtr.Zero) + throw new ArgumentException("window", "Must be a valid window."); - ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); - Wgl.Imports.MakeCurrent(window.DeviceContext, temp_context.Handle); - Wgl.LoadAll(); - Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); - Wgl.DeleteContext(temp_context.Handle); - wgl_loaded = true; - } + Mode = format; - if (Wgl.Delegates.wglCreateContextAttribsARB != null) - { - try + Debug.Print("OpenGL will be bound to handle: {0}", window.WindowHandle); + Debug.Write("Setting pixel format... "); + this.SetGraphicsModePFD(format, (WinWindowInfo)window); + + if (!wgl_loaded) { - Debug.Write("Using WGL_ARB_create_context... "); + // We need to create a temp context in order to load wgl extensions (e.g. for multisampling or GL3). + // We cannot rely on OpenTK.Platform.Wgl until we create the context and call Wgl.LoadAll(). + Debug.Print("Creating temporary context for wgl extensions."); - List attributes = new List(); - attributes.Add((int)ArbCreateContext.MajorVersion); - attributes.Add(major); - attributes.Add((int)ArbCreateContext.MinorVersion); - attributes.Add(minor); - if (flags != 0) - { - attributes.Add((int)ArbCreateContext.Flags); - attributes.Add((int)flags); - } - attributes.Add(0); - - Handle = new ContextHandle( - Wgl.Arb.CreateContextAttribs( - window.DeviceContext, - sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero, - attributes.ToArray())); - if (Handle == ContextHandle.Zero) - Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); - else - Debug.Print("success!"); + ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); + Wgl.Imports.MakeCurrent(window.DeviceContext, temp_context.Handle); + Wgl.LoadAll(); + Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero); + Wgl.DeleteContext(temp_context.Handle); + wgl_loaded = true; + } + + if (Wgl.Delegates.wglCreateContextAttribsARB != null) + { + try + { + Debug.Write("Using WGL_ARB_create_context... "); + + List attributes = new List(); + attributes.Add((int)ArbCreateContext.MajorVersion); + attributes.Add(major); + attributes.Add((int)ArbCreateContext.MinorVersion); + attributes.Add(minor); + if (flags != 0) + { + attributes.Add((int)ArbCreateContext.Flags); +#warning "This is not entirely correct: Embedded is not a valid flag! We need to add a GetARBContextFlags(GraphicsContextFlags) method." + attributes.Add((int)flags); + } + attributes.Add(0); + + Handle = new ContextHandle( + Wgl.Arb.CreateContextAttribs( + window.DeviceContext, + sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero, + attributes.ToArray())); + if (Handle == ContextHandle.Zero) + Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); + else + Debug.Print("success!"); + } + catch (EntryPointNotFoundException e) { Debug.Print(e.ToString()); } + catch (NullReferenceException e) { Debug.Print(e.ToString()); } } - catch (EntryPointNotFoundException e) { Debug.Print(e.ToString()); } - catch (NullReferenceException e) { Debug.Print(e.ToString()); } - } - if (Handle == ContextHandle.Zero) - { - // Failed to create GL3-level context, fall back to GL2. - Debug.Write("Falling back to GL2... "); - Handle = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); if (Handle == ContextHandle.Zero) + { + // Failed to create GL3-level context, fall back to GL2. + Debug.Write("Falling back to GL2... "); Handle = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); - if (Handle == ContextHandle.Zero) - throw new GraphicsContextException( - String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", - Marshal.GetLastWin32Error())); - } + if (Handle == ContextHandle.Zero) + Handle = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); + if (Handle == ContextHandle.Zero) + throw new GraphicsContextException( + String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", + Marshal.GetLastWin32Error())); + } - Debug.WriteLine(String.Format("success! (id: {0})", Handle)); + Debug.WriteLine(String.Format("success! (id: {0})", Handle)); - if (sharedContext != null) - { - Debug.Print("Sharing state with context {0}", sharedContext.ToString()); - Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); + if (sharedContext != null) + { + Debug.Print("Sharing state with context {0}", sharedContext.ToString()); + Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); + } } }