[Graphics] Improved support for external OpenGL contexts

When combining OpenTK with a third-party OpenGL toolkit, it is now
possible to implement a suitable GetAddress() and GetCurrentContext()
implementation in terms of the third-party toolkit. If no
implementation is specified, then OpenTK will try to guess the most
suitable implementation within its own platform backends.

If no custom implementation is defined, and if no suitable
implementation can be found, then OpenTK will throw a
PlatformNotSupportedException. If a suitable implementation is found or
defined, then OpenTK will attempt to load OpenGL entry points using
that implementation.

In this case third-party toolkit remains solely responsible for
managing its context via its MakeCurrent(), SwapBuffers(), etc
implementations.
This commit is contained in:
thefiddler 2013-12-28 01:14:59 +01:00
parent 5cebaccfca
commit 04e6d9335a

View file

@ -39,14 +39,13 @@ namespace OpenTK.Graphics
/// </summary>
public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal
{
public delegate IntPtr GetAddressDelegate(string function);
public delegate ContextHandle GetCurrentContextDelegate();
#region --- Fields ---
IGraphicsContext implementation; // The actual render context implementation for the underlying platform.
bool disposed;
// Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#.
// In this case, We'll assume that the external program will manage the lifetime of this
// context - we'll not destroy it manually.
readonly bool IsExternal;
bool check_errors = true;
// Cache for the context handle. We need this for RemoveContext()
// in case the user does not call Dispose(). When this happens,
@ -67,17 +66,6 @@ namespace OpenTK.Graphics
#region --- Constructors ---
// Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method).
GraphicsContext(ContextHandle handle)
{
implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
lock (SyncRoot)
{
AddContext(this);
}
}
/// <summary>
/// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window.
/// </summary>
@ -163,12 +151,65 @@ namespace OpenTK.Graphics
}
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenTK.Graphics.GraphicsContext"/> class using
/// an external context handle that was created by a third-party library.
/// </summary>
/// <param name="handle">
/// A valid, unique handle for an external OpenGL context, or <c>ContextHandle.Zero</c> to use the current context.
/// It is an error to specify a handle that has been created through OpenTK or that has been passed to OpenTK before.
/// </param>
/// <param name="getAddress">
/// A <c>GetAddressDelegate</c> instance that accepts the name of an OpenGL function and returns
/// a valid function pointer, or <c>IntPtr.Zero</c> if that function is not supported. This delegate should be
/// implemented using the same toolkit that created the OpenGL context (i.e. if the context was created with
/// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetProcAddress() to retrieve function
/// pointers.)
/// </param>
/// <param name="getCurrent">
/// A <c>GetCurrentContextDelegate</c> instance that returns the handle of the current OpenGL context,
/// or <c>IntPtr.Zero</c> if no context is current on the calling thread. This delegate should be implemented
/// using the same toolkit that created the OpenGL context (i.e. if the context was created with
/// SDL_GL_CreateContext(), then this delegate should use SDL_GL_GetCurrentContext() to retrieve
/// the current context.)
/// </param>
public GraphicsContext(ContextHandle handle, GetAddressDelegate getAddress, GetCurrentContextDelegate getCurrent)
{
if (getAddress == null || getCurrent == null)
throw new ArgumentNullException();
lock (SyncRoot)
{
// Replace a zero-handle by the current context, if any
if (handle == ContextHandle.Zero)
{
handle = getCurrent();
}
// Make sure this handle corresponds to a valid, unique OpenGL context
if (handle == ContextHandle.Zero)
{
throw new GraphicsContextMissingException();
}
else if (available_contexts.ContainsKey(handle))
{
throw new InvalidOperationException("Context handle has already been added");
}
// We have a valid handle for an external OpenGL context, wrap it into a
// DummyGLContext instance.
implementation = new Platform.Dummy.DummyGLContext(handle, getAddress);
GetCurrentContext = getCurrent ?? GetCurrentContext;
AddContext(this);
}
implementation.LoadAll();
}
/// <summary>
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
/// </summary>
/// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
/// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
/// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
/// <param name="window">This parameter is reserved.</param>
public GraphicsContext(ContextHandle handle, IWindowInfo window)
: this(handle, window, null, 1, 0, GraphicsContextFlags.Default)
{ }
@ -177,40 +218,14 @@ namespace OpenTK.Graphics
/// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
/// </summary>
/// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
/// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
/// <param name="shareContext">A different context that shares resources with this instance, if any.
/// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct.</param>
/// <param name="major">The major version of the context (e.g. "2" for "2.1").</param>
/// <param name="minor">The minor version of the context (e.g. "1" for "2.1").</param>
/// <param name="flags">A bitwise combination of <see cref="GraphicsContextFlags"/> that describe this context.</param>
/// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
/// <param name="window">This parameter is reserved.</param>
/// <param name="shareContext">This parameter is reserved.</param>
/// <param name="major">This parameter is reserved.</param>
/// <param name="minor">This parameter is reserved.</param>
/// <param name="flags">This parameter is reserved..</param>
public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags)
: this(handle)
{
lock (SyncRoot)
{
IsExternal = true;
if (handle == ContextHandle.Zero)
{
implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
}
else if (available_contexts.ContainsKey(handle))
{
throw new GraphicsContextException("Context already exists.");
}
else
{
switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
{
case false: implementation = Factory.Default.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
case true: implementation = Factory.Embedded.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
}
}
(this as IGraphicsContextInternal).LoadAll();
}
}
: this(handle, Platform.Utilities.CreateGetAddress(), Factory.Default.CreateGetCurrentGraphicsContext())
{ }
#endregion
@ -309,6 +324,7 @@ namespace OpenTK.Graphics
/// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
/// <para>This method requires that a context is current on the calling thread.</para>
/// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext()
{
ContextHandle handle = GetCurrentContext();
@ -326,12 +342,13 @@ namespace OpenTK.Graphics
/// <remarks>
/// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
/// </remarks>
[Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")]
public static GraphicsContext CreateDummyContext(ContextHandle handle)
{
if (handle == ContextHandle.Zero)
throw new ArgumentOutOfRangeException("handle");
return new GraphicsContext(handle);
return new GraphicsContext(handle, (IWindowInfo)null);
}
#endregion
@ -352,7 +369,6 @@ namespace OpenTK.Graphics
#region public static IGraphicsContext CurrentContext
internal delegate ContextHandle GetCurrentContextDelegate();
internal static GetCurrentContextDelegate GetCurrentContext;
/// <summary>