Serialize context construction to avoid threading issues with shared contexts.

This commit is contained in:
the_fiddler 2009-11-03 23:39:03 +00:00
parent 7e1ebfea6b
commit 544cf9d405
2 changed files with 123 additions and 114 deletions

View file

@ -32,7 +32,7 @@ namespace OpenTK.Graphics
static bool share_contexts = true; static bool share_contexts = true;
static bool direct_rendering = 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. // Maps OS-specific context handles to GraphicsContext weak references.
readonly static Dictionary<ContextHandle, WeakReference> available_contexts = new Dictionary<ContextHandle, WeakReference>(); readonly static Dictionary<ContextHandle, WeakReference> available_contexts = new Dictionary<ContextHandle, WeakReference>();
@ -50,7 +50,7 @@ namespace OpenTK.Graphics
{ {
implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle); implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
lock (context_lock) lock (SyncRoot)
{ {
available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this)); available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this));
} }
@ -78,31 +78,31 @@ namespace OpenTK.Graphics
/// </remarks> /// </remarks>
public GraphicsContext(GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags) public GraphicsContext(GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags)
{ {
bool designMode = false; lock (SyncRoot)
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
{ {
Debug.Indent(); bool designMode = false;
Debug.Print("GraphicsMode: {0}", mode); if (mode == null && window == null)
Debug.Print("IWindowInfo: {0}", window); designMode = true;
Debug.Print("GraphicsContextFlags: {0}", flags); else if (mode == null) throw new ArgumentNullException("mode", "Must be a valid GraphicsMode.");
Debug.Print("Requested version: {0}.{1}", major, minor); else if (window == null) throw new ArgumentNullException("window", "Must point to a valid window.");
IGraphicsContext shareContext = null; // Silently ignore invalid major and minor versions.
if (GraphicsContext.ShareContexts) 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. // A small hack to create a shared context with the first available context.
foreach (WeakReference r in GraphicsContext.available_contexts.Values) foreach (WeakReference r in GraphicsContext.available_contexts.Values)
@ -111,26 +111,23 @@ namespace OpenTK.Graphics
break; break;
} }
} }
}
// Todo: Add a DummyFactory implementing IPlatformFactory. // Todo: Add a DummyFactory implementing IPlatformFactory.
if (designMode) if (designMode)
implementation = new Platform.Dummy.DummyGLContext(); implementation = new Platform.Dummy.DummyGLContext();
else else
switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded) switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
{ {
case false: implementation = Factory.Default.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags); break; 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; 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)); available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this));
} }
} finally
finally {
{ Debug.Unindent();
Debug.Unindent(); }
} }
} }
@ -201,7 +198,7 @@ namespace OpenTK.Graphics
{ {
get get
{ {
lock (context_lock) lock (SyncRoot)
{ {
if (available_contexts.Count > 0) if (available_contexts.Count > 0)
{ {
@ -282,7 +279,7 @@ namespace OpenTK.Graphics
/// </remarks> /// </remarks>
void CreateContext(bool direct, IGraphicsContext source) void CreateContext(bool direct, IGraphicsContext source)
{ {
lock (context_lock) lock (SyncRoot)
{ {
available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this)); available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this));
} }
@ -418,7 +415,7 @@ namespace OpenTK.Graphics
if (!IsDisposed) if (!IsDisposed)
{ {
Debug.Print("Disposing context {0}.", (this as IGraphicsContextInternal).Context.ToString()); Debug.Print("Disposing context {0}.", (this as IGraphicsContextInternal).Context.ToString());
lock (context_lock) lock (SyncRoot)
{ {
available_contexts.Remove((this as IGraphicsContextInternal).Context); available_contexts.Remove((this as IGraphicsContextInternal).Context);
} }

View file

@ -26,6 +26,8 @@ namespace OpenTK.Platform.Windows
/// </summary> /// </summary>
internal sealed class WinGLContext : DesktopGraphicsContext internal sealed class WinGLContext : DesktopGraphicsContext
{ {
static object SyncRoot = new object();
static IntPtr opengl32Handle; static IntPtr opengl32Handle;
static bool wgl_loaded; static bool wgl_loaded;
const string opengl32Name = "OPENGL32.DLL"; const string opengl32Name = "OPENGL32.DLL";
@ -36,96 +38,106 @@ namespace OpenTK.Platform.Windows
static WinGLContext() static WinGLContext()
{ {
// Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl. lock (SyncRoot)
if (opengl32Handle == IntPtr.Zero)
{ {
opengl32Handle = Functions.LoadLibrary(opengl32Name); // Dynamically load the OpenGL32.dll in order to use the extension loading capabilities of Wgl.
if (opengl32Handle == IntPtr.Zero) if (opengl32Handle == IntPtr.Zero)
throw new ApplicationException(String.Format("LoadLibrary(\"{0}\") call failed with code {1}", {
opengl32Name, Marshal.GetLastWin32Error())); opengl32Handle = Functions.LoadLibrary(opengl32Name);
Debug.WriteLine(String.Format("Loaded opengl32.dll: {0}", opengl32Handle)); 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, public WinGLContext(GraphicsMode format, WinWindowInfo window, IGraphicsContext sharedContext,
int major, int minor, GraphicsContextFlags flags) int major, int minor, GraphicsContextFlags flags)
{ {
if (window == null) // There are many ways this code can break when accessed by multiple threads. The biggest offender is
throw new ArgumentNullException("window", "Must point to a valid window."); // the sharedContext stuff, which will only become valid *after* this constructor returns.
if (window.WindowHandle == IntPtr.Zero) // The easiest solution is to serialize all context construction - hence the big lock, below.
throw new ArgumentException("window", "Must be a valid window."); lock (SyncRoot)
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)
{ {
// We need to create a temp context in order to load wgl extensions (e.g. for multisampling or GL3). if (window == null)
// We cannot rely on OpenTK.Platform.Wgl until we create the context and call Wgl.LoadAll(). throw new ArgumentNullException("window", "Must point to a valid window.");
Debug.Print("Creating temporary context for wgl extensions."); if (window.WindowHandle == IntPtr.Zero)
throw new ArgumentException("window", "Must be a valid window.");
ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext)); Mode = format;
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) Debug.Print("OpenGL will be bound to handle: {0}", window.WindowHandle);
{ Debug.Write("Setting pixel format... ");
try 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<int> attributes = new List<int>(); ContextHandle temp_context = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext));
attributes.Add((int)ArbCreateContext.MajorVersion); Wgl.Imports.MakeCurrent(window.DeviceContext, temp_context.Handle);
attributes.Add(major); Wgl.LoadAll();
attributes.Add((int)ArbCreateContext.MinorVersion); Wgl.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
attributes.Add(minor); Wgl.DeleteContext(temp_context.Handle);
if (flags != 0) wgl_loaded = true;
{ }
attributes.Add((int)ArbCreateContext.Flags);
attributes.Add((int)flags); if (Wgl.Delegates.wglCreateContextAttribsARB != null)
} {
attributes.Add(0); try
{
Handle = new ContextHandle( Debug.Write("Using WGL_ARB_create_context... ");
Wgl.Arb.CreateContextAttribs(
window.DeviceContext, List<int> attributes = new List<int>();
sharedContext != null ? (sharedContext as IGraphicsContextInternal).Context.Handle : IntPtr.Zero, attributes.Add((int)ArbCreateContext.MajorVersion);
attributes.ToArray())); attributes.Add(major);
if (Handle == ContextHandle.Zero) attributes.Add((int)ArbCreateContext.MinorVersion);
Debug.Print("failed. (Error: {0})", Marshal.GetLastWin32Error()); attributes.Add(minor);
else if (flags != 0)
Debug.Print("success!"); {
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) 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)); Handle = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext));
if (Handle == ContextHandle.Zero) if (Handle == ContextHandle.Zero)
throw new GraphicsContextException( Handle = new ContextHandle(Wgl.Imports.CreateContext(window.DeviceContext));
String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", if (Handle == ContextHandle.Zero)
Marshal.GetLastWin32Error())); 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) if (sharedContext != null)
{ {
Debug.Print("Sharing state with context {0}", sharedContext.ToString()); Debug.Print("Sharing state with context {0}", sharedContext.ToString());
Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle); Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, Handle.Handle);
}
} }
} }