diff --git a/Source/Examples/OpenTK.Examples.csproj b/Source/Examples/OpenTK.Examples.csproj index 4871f240..c78290e6 100644 --- a/Source/Examples/OpenTK.Examples.csproj +++ b/Source/Examples/OpenTK.Examples.csproj @@ -565,6 +565,7 @@ Dependencies\x64\libSDL2.dylib + diff --git a/Source/Examples/OpenTK/Test/ExternalContext.cs b/Source/Examples/OpenTK/Test/ExternalContext.cs new file mode 100644 index 00000000..cdb6d156 --- /dev/null +++ b/Source/Examples/OpenTK/Test/ExternalContext.cs @@ -0,0 +1,81 @@ +// This code was written for the OpenTK library and has been released +// to the Public Domain. +// It is provided "as is" without express or implied warranty of any kind. + +using System; +using System.Runtime.InteropServices; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Graphics.OpenGL; + +namespace Examples.Tests +{ + [Example("External Context Test", ExampleCategory.OpenTK, "OpenGL")] + class ExternalContext + { + public static void Main() + { + using (Toolkit.Init(new ToolkitOptions { Backend = PlatformBackend.PreferNative })) + { + var window = Sdl2.CreateWindow("Test", 0, 0, 640, 480, WindowFlags.AllowHighDpi | WindowFlags.OpenGL); + var context = Sdl2.CreateContext(window); + Sdl2.MakeCurrent(window, context); + + using (var dummy = new GraphicsContext(new ContextHandle(context), OpenTK.Platform.Utilities.CreateDummyWindowInfo())) + { + for (int i = 0; i < 100; i++) + { + Sdl2.PumpEvents(); + GL.ClearColor(i / 100.0f, i / 100.0f, i / 100.0f, i / 100.0f); + GL.Clear(ClearBufferMask.ColorBufferBit); + + Sdl2.SwapWindow(window); + } + + Sdl2.DestroyWindow(window); + } + } + } + } + + #region SDL2 bindings + + public enum WindowFlags + { + Default = 0, + OpenGL = 0x00000002, + AllowHighDpi = 0x00002000, + } + + static class Sdl2 + { + const string lib = "SDL2.dll"; + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateWindow", ExactSpelling = true)] + public static extern IntPtr CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_CreateContext", ExactSpelling = true)] + public static extern IntPtr CreateContext(IntPtr window); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow", ExactSpelling = true)] + public static extern void DestroyWindow(IntPtr window); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)] + public static extern IntPtr GetCurrentContext(); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetProcAddress", ExactSpelling = true)] + public static extern IntPtr GetAddress(string name); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_MakeCurrent", ExactSpelling = true)] + public static extern int MakeCurrent(IntPtr window, IntPtr context); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_PumpEvents", ExactSpelling = true)] + public static extern void PumpEvents(); + + [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_SwapWindow", ExactSpelling = true)] + public static extern void SwapWindow(IntPtr window); + } + + #endregion + +} diff --git a/Source/OpenTK/Graphics/GraphicsContext.cs b/Source/OpenTK/Graphics/GraphicsContext.cs index 6c0b93f6..1fd39b2d 100644 --- a/Source/OpenTK/Graphics/GraphicsContext.cs +++ b/Source/OpenTK/Graphics/GraphicsContext.cs @@ -39,14 +39,13 @@ namespace OpenTK.Graphics /// 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); - } - } - /// /// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window. /// @@ -163,12 +151,65 @@ namespace OpenTK.Graphics } } + /// + /// Initializes a new instance of the class using + /// an external context handle that was created by a third-party library. + /// + /// + /// A valid, unique handle for an external OpenGL context, or ContextHandle.Zero 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. + /// + /// + /// A GetAddressDelegate instance that accepts the name of an OpenGL function and returns + /// a valid function pointer, or IntPtr.Zero 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.) + /// + /// + /// A GetCurrentContextDelegate instance that returns the handle of the current OpenGL context, + /// or IntPtr.Zero 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.) + /// + 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(); + } + /// /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK. /// /// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK. - /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo. - /// Occurs if handle is identical to a context already registered with OpenTK. + /// This parameter is reserved. 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. /// /// The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK. - /// The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo. - /// 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. - /// The major version of the context (e.g. "2" for "2.1"). - /// The minor version of the context (e.g. "1" for "2.1"). - /// A bitwise combination of that describe this context. - /// Occurs if handle is identical to a context already registered with OpenTK. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved. + /// This parameter is reserved.. 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 /// Instances created by this method will not be functional. Instance methods will have no effect. /// This method requires that a context is current on the calling thread. /// + [Obsolete("Use GraphicsContext(ContextHandle, IWindowInfo) constructor instead")] public static GraphicsContext CreateDummyContext() { ContextHandle handle = GetCurrentContext(); @@ -326,12 +342,13 @@ namespace OpenTK.Graphics /// /// Instances created by this method will not be functional. Instance methods will have no effect. /// + [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; /// diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj index 7bddfcb0..92404b7f 100644 --- a/Source/OpenTK/OpenTK.csproj +++ b/Source/OpenTK/OpenTK.csproj @@ -777,6 +777,7 @@ + diff --git a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs index 05853f79..bb6f1a2a 100644 --- a/Source/OpenTK/Platform/Dummy/DummyGLContext.cs +++ b/Source/OpenTK/Platform/Dummy/DummyGLContext.cs @@ -22,7 +22,8 @@ namespace OpenTK.Platform.Dummy /// internal sealed class DummyGLContext : DesktopGraphicsContext { - // This mode is not real. To receive a real mode we'd have to create a temporary context, which is not desirable! + readonly GraphicsContext.GetAddressDelegate Loader; + bool vsync; int swap_interval; static int handle_count; @@ -31,13 +32,20 @@ namespace OpenTK.Platform.Dummy #region --- Constructors --- public DummyGLContext() - : this(new ContextHandle(new IntPtr(++handle_count))) { + Handle = new ContextHandle( + new IntPtr(Interlocked.Increment( + ref handle_count))); } - - public DummyGLContext(ContextHandle handle) + + public DummyGLContext(ContextHandle handle, GraphicsContext.GetAddressDelegate loader) + : this() { - Handle = handle; + if (handle != ContextHandle.Zero) + { + Handle = handle; + } + Loader = loader; Mode = new GraphicsMode(new IntPtr(2), 32, 16, 0, 0, 0, 2, false); } @@ -45,15 +53,6 @@ namespace OpenTK.Platform.Dummy #region --- IGraphicsContext Members --- - public void CreateContext(bool direct, IGraphicsContext source) - { - if (Handle == ContextHandle.Zero) - { - ++handle_count; - Handle = new ContextHandle((IntPtr)handle_count); - } - } - public override void SwapBuffers() { } public override void MakeCurrent(IWindowInfo info) @@ -81,9 +80,15 @@ namespace OpenTK.Platform.Dummy get { return current_thread != null && current_thread == Thread.CurrentThread; } } - public override IntPtr GetAddress(string function) { return IntPtr.Zero; } + public override IntPtr GetAddress(string function) + { + return Loader(function); + } - public override IntPtr GetAddress(IntPtr function) { return IntPtr.Zero; } + public override IntPtr GetAddress(IntPtr function) + { + return IntPtr.Zero; + } public override int SwapInterval { @@ -101,7 +106,14 @@ namespace OpenTK.Platform.Dummy { } public override void LoadAll() - { } + { + new OpenTK.Graphics.OpenGL.GL().LoadEntryPoints(); + new OpenTK.Graphics.OpenGL4.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES10.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES11.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES20.GL().LoadEntryPoints(); + new OpenTK.Graphics.ES30.GL().LoadEntryPoints(); + } #endregion diff --git a/Source/OpenTK/Platform/MacOS/AglContext.cs b/Source/OpenTK/Platform/MacOS/AglContext.cs index 65bdd0af..d1de6664 100644 --- a/Source/OpenTK/Platform/MacOS/AglContext.cs +++ b/Source/OpenTK/Platform/MacOS/AglContext.cs @@ -464,64 +464,16 @@ namespace OpenTK.Platform.MacOS #region IGraphicsContextInternal Members - private const string Library = "libdl.dylib"; - - [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] - private static extern bool NSIsSymbolNameDefined(string s); - [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] - private static extern bool NSIsSymbolNameDefined(IntPtr s); - [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] - private static extern IntPtr NSLookupAndBindSymbol(string s); - [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] - private static extern IntPtr NSLookupAndBindSymbol(IntPtr s); - [DllImport(Library, EntryPoint = "NSAddressOfSymbol")] - private static extern IntPtr NSAddressOfSymbol(IntPtr symbol); - public override IntPtr GetAddress(string function) { - // Instead of allocating and combining strings in managed memory - // we do that directly in unmanaged memory. This way, we avoid - // 2 string allocations every time this function is called. - - // must add a '_' prefix and null-terminate the function name, - // hence we allocate +2 bytes - IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2); - try - { - Marshal.WriteByte(ptr, (byte)'_'); - for (int i = 0; i < function.Length; i++) - { - Marshal.WriteByte(ptr, i + 1, (byte)function[i]); - } - Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate - - IntPtr symbol = IntPtr.Zero; - if (NSIsSymbolNameDefined(ptr)) - { - symbol = NSLookupAndBindSymbol(ptr); - if (symbol != IntPtr.Zero) - symbol = NSAddressOfSymbol(symbol); - } - return symbol; - } - finally - { - Marshal.FreeHGlobal(ptr); - } + return NS.GetAddress(function); } public override IntPtr GetAddress(IntPtr function) { - if (!NSIsSymbolNameDefined(function)) - return IntPtr.Zero; - - IntPtr symbol = NSLookupAndBindSymbol(function); - if (symbol != IntPtr.Zero) - symbol = NSAddressOfSymbol(symbol); - - return symbol; + return NS.GetAddress(function); } - + #endregion } } diff --git a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs index ad540311..97473c23 100644 --- a/Source/OpenTK/Platform/MacOS/MacOSFactory.cs +++ b/Source/OpenTK/Platform/MacOS/MacOSFactory.cs @@ -70,7 +70,7 @@ namespace OpenTK.Platform.MacOS { return (GraphicsContext.GetCurrentContextDelegate)delegate { - return new ContextHandle(Agl.aglGetCurrentContext()); + return new ContextHandle(Cgl.GetCurrentContext()); }; } diff --git a/Source/OpenTK/Platform/MacOS/NS.cs b/Source/OpenTK/Platform/MacOS/NS.cs new file mode 100644 index 00000000..945a7e25 --- /dev/null +++ b/Source/OpenTK/Platform/MacOS/NS.cs @@ -0,0 +1,92 @@ +#region License +// +// NS.cs +// +// Author: +// Stefanos A. +// +// Copyright (c) 2006-2013 Stefanos Apostolopoulos +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#endregion + +using System; +using System.Runtime.InteropServices; + +namespace OpenTK.Platform.MacOS +{ + + internal class NS + { + const string Library = "libdl.dylib"; + + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + static extern bool NSIsSymbolNameDefined(string s); + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + static extern bool NSIsSymbolNameDefined(IntPtr s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + static extern IntPtr NSLookupAndBindSymbol(string s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + static extern IntPtr NSLookupAndBindSymbol(IntPtr s); + [DllImport(Library, EntryPoint = "NSAddressOfSymbol")] + static extern IntPtr NSAddressOfSymbol(IntPtr symbol); + + public static IntPtr GetAddress(string function) + { + // Instead of allocating and combining strings in managed memory + // we do that directly in unmanaged memory. This way, we avoid + // 2 string allocations every time this function is called. + + // must add a '_' prefix and null-terminate the function name, + // hence we allocate +2 bytes + IntPtr ptr = Marshal.AllocHGlobal(function.Length + 2); + try + { + Marshal.WriteByte(ptr, (byte)'_'); + for (int i = 0; i < function.Length; i++) + { + Marshal.WriteByte(ptr, i + 1, (byte)function[i]); + } + Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate + + IntPtr symbol = GetAddress(ptr); + return symbol; + } + finally + { + Marshal.FreeHGlobal(ptr); + } + } + + public static IntPtr GetAddress(IntPtr function) + { + IntPtr symbol = IntPtr.Zero; + if (NSIsSymbolNameDefined(function)) + { + symbol = NSLookupAndBindSymbol(function); + if (symbol != IntPtr.Zero) + symbol = NSAddressOfSymbol(symbol); + } + return symbol; + } + } +} + diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs index 9bf07558..40ec3700 100644 --- a/Source/OpenTK/Platform/SDL2/Sdl2.cs +++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs @@ -334,6 +334,7 @@ namespace OpenTK.Platform.SDL2 [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetAttribute", ExactSpelling = true)] public static extern int GetAttribute(ContextAttribute attr, out int value); + [SuppressUnmanagedCodeSecurity] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GL_GetCurrentContext", ExactSpelling = true)] public static extern IntPtr GetCurrentContext(); diff --git a/Source/OpenTK/Platform/Utilities.cs b/Source/OpenTK/Platform/Utilities.cs index 4ed38158..67c92a78 100644 --- a/Source/OpenTK/Platform/Utilities.cs +++ b/Source/OpenTK/Platform/Utilities.cs @@ -170,6 +170,32 @@ namespace OpenTK.Platform #endregion + #region CreateGetAddress + + internal static GraphicsContext.GetAddressDelegate CreateGetAddress() + { + GraphicsContext.GetAddressDelegate loader = null; + if (Configuration.RunningOnWindows) + { + loader = Platform.Windows.Wgl.GetProcAddress; + } + else if (Configuration.RunningOnX11) + { + loader = Platform.X11.Glx.GetProcAddress; + } + else if (Configuration.RunningOnMacOS) + { + loader = Platform.MacOS.NS.GetAddress; + } + else + { + throw new PlatformNotSupportedException(); + } + return loader; + } + + #endregion + #region --- Creating a Graphics Context --- ///