diff --git a/Source/OpenTK/Platform/MacOS/CocoaContext.cs b/Source/OpenTK/Platform/MacOS/CocoaContext.cs index c752485f..342f68b4 100644 --- a/Source/OpenTK/Platform/MacOS/CocoaContext.cs +++ b/Source/OpenTK/Platform/MacOS/CocoaContext.cs @@ -47,6 +47,12 @@ namespace OpenTK static readonly IntPtr selFlushBuffer = Selector.Get("flushBuffer"); static readonly IntPtr selMakeCurrentContext = Selector.Get("makeCurrentContext"); static readonly IntPtr selUpdate = Selector.Get("update"); + static readonly IntPtr opengl = NS.AddImage( + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + AddImageFlags.ReturnOnError); + static readonly IntPtr opengles = NS.AddImage( + "/System/Library/Frameworks/OpenGL.framework/OpenGLES", + AddImageFlags.ReturnOnError); static CocoaContext() { @@ -292,14 +298,49 @@ namespace OpenTK #region IGraphicsContextInternal Members - public override IntPtr GetAddress(string function) - { - return NS.GetAddress(function); - } - public override IntPtr GetAddress(IntPtr function) { - return NS.GetAddress(function); + unsafe + { + // Add a leading underscore to the function name + // As of OpenGL 4.4, all functions are < 64 bytes + // in length. Double that just to be sure. + const int max = 128; + byte* fun = stackalloc byte[max]; + byte* ptr = fun; + byte* cur = (byte*)function.ToPointer(); + int i = 0; + + *ptr++ = (byte)'_'; + while (*cur != 0 && ++i < max) + { + *ptr++ = *cur++; + } + + if (i >= max - 1) + { + Debug.Print("Function {0} too long. Loading will fail.", + Marshal.PtrToStringAnsi(function)); + } + + IntPtr address = IntPtr.Zero; + IntPtr symbol = IntPtr.Zero; + if (opengl != IntPtr.Zero) + { + symbol = NS.LookupSymbolInImage(opengl, new IntPtr(fun), + SymbolLookupFlags.Bind | SymbolLookupFlags.ReturnOnError); + } + if (symbol == IntPtr.Zero && opengles != IntPtr.Zero) + { + symbol = NS.LookupSymbolInImage(opengles, new IntPtr(fun), + SymbolLookupFlags.Bind | SymbolLookupFlags.ReturnOnError); + } + if (symbol != IntPtr.Zero) + { + address = NS.AddressOfSymbol(symbol); + } + return address; + } } #endregion diff --git a/Source/OpenTK/Platform/MacOS/NS.cs b/Source/OpenTK/Platform/MacOS/NS.cs index a23a8179..90c5d71e 100644 --- a/Source/OpenTK/Platform/MacOS/NS.cs +++ b/Source/OpenTK/Platform/MacOS/NS.cs @@ -33,27 +33,52 @@ using System.Runtime.InteropServices; namespace OpenTK.Platform.MacOS { + [Flags] + enum AddImageFlags + { + ReturnOnError = 1, + WithSearching = 2, + ReturnOnlyIfLoaded = 4 + } + + [Flags] + enum SymbolLookupFlags + { + Bind = 0, + BindNow = 1, + BindFully = 2, + ReturnOnError = 4 + } 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 = "NSAddImage")] + internal static extern IntPtr AddImage(string s, AddImageFlags flags); [DllImport(Library, EntryPoint = "NSAddressOfSymbol")] - static extern IntPtr NSAddressOfSymbol(IntPtr symbol); + internal static extern IntPtr AddressOfSymbol(IntPtr symbol); + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + internal static extern bool IsSymbolNameDefined(string s); + [DllImport(Library, EntryPoint = "NSIsSymbolNameDefined")] + internal static extern bool IsSymbolNameDefined(IntPtr s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + internal static extern IntPtr LookupAndBindSymbol(string s); + [DllImport(Library, EntryPoint = "NSLookupAndBindSymbol")] + internal static extern IntPtr LookupAndBindSymbol(IntPtr s); + [DllImport(Library, EntryPoint = "NSLookupSymbolInImage")] + internal static extern IntPtr LookupSymbolInImage(IntPtr image, IntPtr symbolName, SymbolLookupFlags options); + + // Unfortunately, these are slower even if they are more + // portable and simpler to use. [DllImport(Library)] - private static extern IntPtr dlopen(String fileName, int flags); + internal static extern IntPtr dlopen(String fileName, int flags); [DllImport(Library)] - private static extern int dlclose(IntPtr handle); + internal static extern int dlclose(IntPtr handle); [DllImport (Library)] - private static extern IntPtr dlsym (IntPtr handle, string symbol); + internal static extern IntPtr dlsym (IntPtr handle, string symbol); + [DllImport (Library)] + internal static extern IntPtr dlsym (IntPtr handle, IntPtr symbol); public static IntPtr GetAddress(string function) { @@ -73,7 +98,7 @@ namespace OpenTK.Platform.MacOS } Marshal.WriteByte(ptr, function.Length + 1, 0); // null-terminate - IntPtr symbol = GetAddress(ptr); + IntPtr symbol = GetAddressInternal(ptr); return symbol; } finally @@ -84,12 +109,39 @@ namespace OpenTK.Platform.MacOS public static IntPtr GetAddress(IntPtr function) { - IntPtr symbol = IntPtr.Zero; - if (NSIsSymbolNameDefined(function)) + unsafe { - symbol = NSLookupAndBindSymbol(function); + const int max = 64; + byte* symbol = stackalloc byte[max]; + byte* ptr = symbol; + byte* cur = (byte*)function.ToPointer(); + int i = 0; + + *ptr++ = (byte)'_'; + while (*cur != 0 && ++i < max) + { + *ptr++ = *cur++; + } + + if (i >= max - 1) + { + throw new NotSupportedException(String.Format( + "Function {0} is too long. Please report a bug at https://github.com/opentk/issues/issues", + Marshal.PtrToStringAnsi(function))); + } + + return GetAddressInternal(new IntPtr(symbol)); + } + } + + static IntPtr GetAddressInternal(IntPtr function) + { + IntPtr symbol = IntPtr.Zero; + if (IsSymbolNameDefined(function)) + { + symbol = LookupAndBindSymbol(function); if (symbol != IntPtr.Zero) - symbol = NSAddressOfSymbol(symbol); + symbol = AddressOfSymbol(symbol); } return symbol; } @@ -99,6 +151,11 @@ namespace OpenTK.Platform.MacOS return dlsym(handle, symbol); } + public static IntPtr GetSymbol(IntPtr handle, IntPtr symbol) + { + return dlsym(handle, symbol); + } + public static IntPtr LoadLibrary(string fileName) { const int RTLD_NOW = 2;