[Mac] Sped up extension loading

From ~200ms down to ~65ms on a rMBP with Nvidia 650M and Mac OS X
10.9.2.
This commit is contained in:
thefiddler 2014-04-26 14:21:26 +02:00
parent 433fa35f7e
commit b732e377c9
2 changed files with 121 additions and 23 deletions

View file

@ -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

View file

@ -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;