#region --- License --- /* Copyright (c) 2006, 2007 Stefanos Apostolopoulos * See license.txt for license info */ #endregion #region --- Using Directives --- using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Reflection; #endregion [assembly: CLSCompliant(true)] namespace OpenTK.OpenGL { /// /// OpenGL binding for .NET, implementing OpenGL 2.1, plus extensions. /// /// /// /// OpenTK.OpenGL.GL contains all OpenGL enums and functions defined in the 2.1 specification. /// The official .spec files can be found at: http://opengl.org/registry/. /// /// /// OpenTK.OpenGL.GL relies on static initialization to obtain the entry points for OpenGL functions. /// Please ensure that a valid OpenGL context has been made current in the pertinent thread before /// any OpenGL functions are called (toolkits like GLUT, SDL or GLFW will automatically take care of /// the context initialization process). Without a valid OpenGL context, OpenTK.OpenGL.GL will only be able /// to retrieve statically exported entry points (typically corresponding to OpenGL version 1.1 under Windows, /// 1.3 under Linux and 1.4 under Windows Vista), and extension methods will need to be loaded manually. /// /// /// If you prefer not to rely on static initialisation for the Gl class, you can use the /// ReloadFunctions or ReloadFunction methods to manually force the initialisation of OpenGL entry points. /// The ReloadFunctions method should be called whenever you change an existing visual or pixelformat. This /// generally happens when you change the color/stencil/depth buffer associated with a window (but probably /// not the resolution). This may or may not be necessary under Linux/MacOS, but is generally required for /// Windows. /// /// /// You can use the Gl.IsExtensionSupported method to check whether any given category of extension functions /// exists in the current OpenGL context. The results can be cached to speed up future searches. /// Keep in mind that different OpenGL contexts may support different extensions, and under different entry /// points. Always check if all required extensions are still supported when changing visuals or pixel /// formats. /// /// /// You may retrieve the entry point for an OpenGL extension using the Gl.GetDelegateForExtensionMethod /// and Gl.GetFunctionPointerForExtensionMethod methods. You may retrieve the entry point for an OpenGL /// function using the Gl.GetDelegateForMethod method. All three methods are cross-platform. /// /// /// /// /// /// /// /// /// /// /// public static partial class GL { #region internal string Library internal const string Library = "opengl32.dll"; #endregion #region private enum Platform /// /// Enumerates the platforms OpenTK can run on. /// private enum Platform { Unknown, Windows, X11, X11_ARB, OSX }; #endregion private enum Platform private static Platform platform = Platform.Unknown; private static System.Collections.Generic.Dictionary AvailableExtensions = new Dictionary(); #region internal static extern IntPtr glxGetProcAddressARB(string s); // also linux, for our ARB-y friends [DllImport(Library, EntryPoint = "glXGetProcAddressARB")] internal static extern IntPtr glxGetProcAddressARB(string s); #endregion #region internal static IntPtr aglGetProcAddress(string s) // osx gets complicated [DllImport("libdl.dylib", EntryPoint = "NSIsSymbolNameDefined")] internal static extern bool NSIsSymbolNameDefined(string s); [DllImport("libdl.dylib", EntryPoint = "NSLookupAndBindSymbol")] internal static extern IntPtr NSLookupAndBindSymbol(string s); [DllImport("libdl.dylib", EntryPoint = "NSAddressOfSymbol")] internal static extern IntPtr NSAddressOfSymbol(IntPtr symbol); internal static IntPtr aglGetProcAddress(string s) { string fname = "_" + s; if (!NSIsSymbolNameDefined(fname)) return IntPtr.Zero; IntPtr symbol = NSLookupAndBindSymbol(fname); if (symbol != IntPtr.Zero) symbol = NSAddressOfSymbol(symbol); return symbol; } #endregion #region public static IntPtr GetFunctionPointerForExtensionMethod(string name) /// /// Retrieves the entry point for a dynamically exported OpenGL function. /// /// The function string for the OpenGL function (eg. "glNewList") /// /// An IntPtr contaning the address for the entry point, or IntPtr.Zero if the specified /// OpenGL function is not dynamically exported. /// /// /// /// The Marshal.GetDelegateForFunctionPointer method can be used to turn the return value /// into a call-able delegate. /// /// /// This function is cross-platform. It determines the underlying platform and uses the /// correct wgl, glx or agl GetAddress function to retrieve the function pointer. /// /// /// /// public static IntPtr GetFunctionPointerForExtensionMethod(string name) { IntPtr result = IntPtr.Zero; switch (platform) { case Platform.Unknown: // WGL? try { result = OpenTK.Platform.Windows.Wgl.GetProcAddress(name); platform = Platform.Windows; return result; } catch (Exception) { } // AGL? (before X11, since GLX might exist on OSX) try { result = aglGetProcAddress(name); platform = Platform.OSX; return result; } catch (Exception) { } // X11? try { result = OpenTK.Platform.X11.Glx.GetProcAddress(name); platform = Platform.X11; return result; } catch (Exception) { } // X11 ARB? try { result = glxGetProcAddressARB(name); platform = Platform.X11_ARB; return result; } catch (Exception) { } // Ack! throw new NotSupportedException( @"Could not find out how to retrive function pointers for this platform. Did you remember to copy OpenTK.OpenGL.dll.config to your binary's folder? "); case Platform.Windows: return OpenTK.Platform.Windows.Wgl.GetProcAddress(name); case Platform.OSX: return aglGetProcAddress(name); case Platform.X11: return OpenTK.Platform.X11.Glx.GetProcAddress(name); case Platform.X11_ARB: return glxGetProcAddressARB(name); } throw new NotSupportedException("Internal error - please report."); } #endregion public static IntPtr GetFunctionPointerForExtensionMethod(string s) #region public static Delegate GetDelegateForExtensionMethod(string name, Type signature) /// /// Creates a System.Delegate that can be used to call a dynamically exported OpenGL function. /// /// The name of the OpenGL function (eg. "glNewList") /// The signature of the OpenGL function. /// /// A System.Delegate that can be used to call this OpenGL function or null /// if the function is not available in the current OpenGL context. /// public static Delegate GetDelegateForExtensionMethod(string name, Type signature) { IntPtr address = GetFunctionPointerForExtensionMethod(name); if (address == IntPtr.Zero || address == new IntPtr(1) || // Workaround for buggy nvidia drivers which return address == new IntPtr(2)) // 1 or 2 instead of IntPtr.Zero for some extensions. { return null; } else { return Marshal.GetDelegateForFunctionPointer(address, signature); } } #endregion public static Delegate GetAddress(string name, Type signature) #region public static Delegate GetDelegateForMethod(string name, Type signature) /// /// Creates a System.Delegate that can be used to call an OpenGL function, core or extension. /// /// The name of the OpenGL function (eg. "glNewList") /// The signature of the OpenGL function. /// /// A System.Delegate that can be used to call this OpenGL function, or null if the specified /// function name did not correspond to an OpenGL function. /// public static Delegate GetDelegateForMethod(string name, Type signature) { Delegate d; if (Assembly.GetExecutingAssembly() .GetType("OpenTK.OpenGL.Imports") .GetMethod(name.Substring(2), BindingFlags.NonPublic | BindingFlags.Static) != null) { d = GetDelegateForExtensionMethod(name, signature) ?? Delegate.CreateDelegate(signature, typeof(Imports), name.Substring(2)); } else { d = GetDelegateForExtensionMethod(name, signature); } return d; } #endregion #region public static bool IsExtensionSupported(string name) /// /// Determines whether the specified OpenGL extension category is available in /// the current OpenGL context. Equivalent to IsExtensionSupported(name, true) /// /// The string for the OpenGL extension category (eg. "GL_ARB_multitexture") /// True if the specified extension is available, false otherwise. public static bool IsExtensionSupported(string name) { return IsExtensionSupported(name, true); } #endregion #region public static bool IsExtensionSupported(string name, bool useCache) /// /// Determines whether the specified OpenGL extension category is available in /// the current OpenGL context. /// /// The string for the OpenGL extension category (eg. "GL_ARB_multitexture") /// If true, the results will be cached to speed up future results. /// True if the specified extension is available, false otherwise. public static bool IsExtensionSupported(string name, bool useCache) { // Use cached results if (useCache) { // Build cache if it is not available. We assume that all drivers // will support at least one extension to opengl 1.0 (for example // opengl 1.1). This should hold true even for software contexts // (microsoft's soft implementation is gl 1.1 compatible), at least // for any system capable of running .Net/Mono. if (AvailableExtensions.Count == 0) { ParseAvailableExtensions(); } // Search the cache for the string. Note that the cache substitutes // strings "1.0" to "2.1" with "GL_VERSION_1_0" to "GL_VERSION_2_1" if (AvailableExtensions.ContainsKey(name)) return AvailableExtensions[name]; return false; } // Do not use cached results string extension_string = GL.GetString(GL.Enums.StringName.EXTENSIONS); if (String.IsNullOrEmpty(extension_string)) return false; // no extensions are available // Check if the user searches for GL_VERSION_X_X and search glGetString(GL_VERSION) instead. if (name.Contains("GL_VERSION_")) { return GL.GetString(OpenTK.OpenGL.GL.Enums.StringName.VERSION).Trim().StartsWith(name.Replace("GL_VERSION_", String.Empty).Replace('_', '.')); } // Search for the string given. string[] extensions = extension_string.Split(' '); foreach (string s in extensions) { if (name == s) return true; } return false; } #endregion #region private static void ParseAvailableExtensions() /// /// Builds a cache of the supported extensions to speed up searches. /// private static void ParseAvailableExtensions() { // Assumes there is a current context. string version_string = GL.GetString(OpenTK.OpenGL.GL.Enums.StringName.VERSION); if (String.IsNullOrEmpty(version_string)) return; // this shoudn't happen string version = version_string.Trim(' '); if (version.StartsWith("1.2")) { AvailableExtensions.Add("VERSION_1_2", true); } else if (version.StartsWith("1.3")) { AvailableExtensions.Add("VERSION_1_2", true); AvailableExtensions.Add("VERSION_1_3", true); } else if (version.StartsWith("1.4")) { AvailableExtensions.Add("VERSION_1_2", true); AvailableExtensions.Add("VERSION_1_3", true); AvailableExtensions.Add("VERSION_1_4", true); } else if (version.StartsWith("1.5")) { AvailableExtensions.Add("VERSION_1_2", true); AvailableExtensions.Add("VERSION_1_3", true); AvailableExtensions.Add("VERSION_1_4", true); AvailableExtensions.Add("VERSION_1_5", true); } else if (version.StartsWith("2.0")) { AvailableExtensions.Add("VERSION_1_2", true); AvailableExtensions.Add("VERSION_1_3", true); AvailableExtensions.Add("VERSION_1_4", true); AvailableExtensions.Add("VERSION_1_5", true); AvailableExtensions.Add("VERSION_2_0", true); } else if (version.StartsWith("2.1")) { AvailableExtensions.Add("VERSION_1_2", true); AvailableExtensions.Add("VERSION_1_3", true); AvailableExtensions.Add("VERSION_1_4", true); AvailableExtensions.Add("VERSION_1_5", true); AvailableExtensions.Add("VERSION_2_0", true); AvailableExtensions.Add("VERSION_2_1", true); } string extension_string = GL.GetString(OpenTK.OpenGL.GL.Enums.StringName.EXTENSIONS); if (String.IsNullOrEmpty(extension_string)) return; // no extensions are available string[] extensions = extension_string.Split(' '); foreach (string ext in extensions) { AvailableExtensions.Add(ext, true); } } #endregion #region public static void ReloadFunctions() /// /// Reloads all OpenGL functions (core and extensions). /// /// /// /// Call this function to reload all OpenGL entry points. This should be done /// whenever you change the pixelformat/visual, or in the case you cannot (or do not want) /// to use the automatic initialisation. /// /// /// Calling this function before the automatic initialisation has taken place will result /// in the Gl class being initialised twice. This is harmless, but given the automatic /// initialisation should be preferred. /// /// public static void ReloadFunctions() { Assembly asm = Assembly.GetExecutingAssembly();//Assembly.Load("OpenTK.OpenGL"); Type delegates_class = asm.GetType("OpenTK.OpenGL.Delegates"); Type imports_class = asm.GetType("OpenTK.OpenGL.Imports"); FieldInfo[] v = delegates_class.GetFields(BindingFlags.Static | BindingFlags.NonPublic); foreach (FieldInfo f in v) { f.SetValue(null, GetDelegateForMethod(f.Name, f.FieldType)); } //ParseAvailableExtensions(); AvailableExtensions.Clear(); } #endregion #region public static bool ReloadFunction(string name) /// /// Tries to reload the given OpenGL function (core or extension). /// /// The name of the OpenGL function (i.e. glShaderSource) /// True if the function was found and reloaded, false otherwise. /// /// /// Use this function if you require greater granularity when loading OpenGL entry points. /// /// /// While the automatic initialisation will load all OpenGL entry points, in some cases /// the initialisation can take place before an OpenGL Context has been established. /// In this case, use this function to load the entry points for the OpenGL functions /// you will need, or use ReloadFunctions() to load all available entry points. /// /// /// This function will return true if the given OpenGL function exists, false otherwise. /// A return value of "true" does not mean that any specific OpenGL function is supported; /// rather it means that the string passed to this function denotes a known OpenGL function. /// /// /// To query supported extensions use the IsExtensionSupported() function instead. /// /// public static bool ReloadFunction(string name) { Assembly asm = Assembly.Load("OpenTK.OpenGL"); Type delegates_class = asm.GetType("OpenTK.OpenGL.Delegates"); Type imports_class = asm.GetType("OpenTK.OpenGL.Imports"); FieldInfo f = delegates_class.GetField(name); if (f == null) return false; f.SetValue(null, GetDelegateForMethod(f.Name, f.FieldType)); return true; } #endregion } }