/// <summary> /// Get the library name used for loading OpenGL functions. /// </summary> /// <param name="version"> /// A <see cref="KhronosVersion"/> that specifies the API to bind. /// </param> /// <returns> /// It returns a <see cref="String"/> that specifies the library name to be used. /// </returns> private static string GetPlatformLibrary(KhronosVersion version) { switch (version.Api) { case KhronosVersion.ApiGl: case KhronosVersion.ApiGlsc2: switch (Platform.CurrentPlatformId) { case Platform.Id.Linux: return("libGL.so.1"); case Platform.Id.MacOS: return("/usr/X11/lib/libGL.1.dylib"); default: // EGL ignore library name return(Library); } case KhronosVersion.ApiGles1: switch (Platform.CurrentPlatformId) { case Platform.Id.Linux: return("libGLESv2.so"); default: return(Egl.IsRequired ? LibraryEs : Library); } case KhronosVersion.ApiGles2: switch (Platform.CurrentPlatformId) { case Platform.Id.Linux: return("libGLESv2.so"); default: return(Egl.IsRequired ? LibraryEs2 : Library); } default: throw new NotSupportedException($"binding API for OpenGL {version} not supported"); } }
/// <summary> /// Query the OpenGL version. Requires some information lookup functions to be bound. /// </summary> /// <returns>The version of the current OpenGL context.</returns> private static KhronosVersion QueryContextVersionInternal() { // Parse version string (effective for detecting Desktop and ES contextes) string str = GetString(StringName.Version); KhronosVersion glVersion = KhronosVersion.Parse(str); // Vendor/Render information CurrentVendor = GetString(StringName.Vendor); CurrentRenderer = GetString(StringName.Renderer); SoftwareRenderer = CurrentRenderer.Contains("llvmpipe"); // Context profile if (glVersion.Api == KhronosVersion.API_GL && glVersion >= Version320) { string glProfile; Get(CONTEXT_PROFILE_MASK, out int ctxProfile); if ((ctxProfile & CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0) { glProfile = KhronosVersion.PROFILE_COMPATIBILITY; } else if ((ctxProfile & CONTEXT_CORE_PROFILE_BIT) != 0) { glProfile = KhronosVersion.PROFILE_CORE; } else { glProfile = KhronosVersion.PROFILE_COMPATIBILITY; } return(new KhronosVersion(glVersion, glProfile)); } else { string profile = KhronosVersion.PROFILE_COMPATIBILITY; if (CurrentRenderer.Contains("WebGL")) { profile = KhronosVersion.PROFILE_WEBGL; } return(new KhronosVersion(glVersion, profile)); } }
/// <summary> /// Determine whether an API implementation supports this feature. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the API version. /// </param> /// <param name="extensions"> /// The <see cref="ExtensionsCollection"/> that specifies the API extensions registry. It can be null. /// </param> /// <returns> /// It returns a <see cref="Boolean"/> that specifies whether this feature is supported by the /// API having the version <paramref name="version"/> and the extensions registry <paramref name="extensions"/>. /// </returns> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="version"/> is null. /// </exception> public bool IsSupported(KhronosVersion version, KhronosApi.ExtensionsCollection extensions) { if (version == null) { throw new ArgumentNullException("version"); } // Feature is an API version? if (FeatureVersion != null) { // API must match // Note: no need or regex, since Api cannot be a pattern if (version.Api != FeatureVersion.Api) { return(false); } // Profile must match, if defined if (Profile != null && version.Profile != null && Regex.IsMatch(version.Profile, Profile) == false) { return(false); } // API version must be greater than or equal to the required version return(version >= FeatureVersion); } // No API version: indeed it is an API extension if (extensions != null) { // Check compatible API // Note: regex is required since extensions can be implemented by multiple APIs if (Regex.IsMatch(version.Api, Api) == false) { return(false); } // Last chance: extension name return(extensions.HasExtensions(FeatureName)); } else { return(false); } }
/// <summary> /// Default constructor. /// </summary> protected NativeSurface() { try { if ((_Display = Egl.GetDisplay(new IntPtr(Egl.DEFAULT_DISPLAY))) == IntPtr.Zero) { throw new InvalidOperationException("unable to get display handle"); } int[] major = new int[1], minor = new int[1]; if (Egl.Initialize(_Display, major, minor) == false) { throw new InvalidOperationException("unable to initialize the display"); } _EglVersion = new KhronosVersion(major[0], minor[0], KhronosVersion.ApiEgl); } catch { Dispose(); throw; } }
/// <summary> /// Query the version of the current OpenGL context. /// </summary> /// <returns> /// It returns the <see cref="KhronosVersion"/> specifying the actual version of the context current on this thread. /// </returns> /// <exception cref="InvalidOperationException"> /// Exception thrown if no GL context is current on the calling thread. /// </exception> public static KhronosVersion QueryContextVersion() { IntPtr ctx = DeviceContext.GetCurrentContext(); if (ctx == IntPtr.Zero) { throw new InvalidOperationException("no current context"); } // Parse version string (effective for detecting Desktop and ES contextes) KhronosVersion glversion = KhronosVersion.Parse(Gl.GetString(StringName.Version)); // Context profile if (glversion.Api == KhronosVersion.ApiGl && glversion >= Gl.Version_320) { string glProfile = null; int ctxProfile = 0; Gl.Get(Gl.CONTEXT_PROFILE_MASK, out ctxProfile); if ((ctxProfile & Gl.CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0) { glProfile = KhronosVersion.ProfileCompatibility; } else if ((ctxProfile & Gl.CONTEXT_CORE_PROFILE_BIT) != 0) { glProfile = KhronosVersion.ProfileCore; } else { glProfile = KhronosVersion.ProfileCompatibility; } return(new KhronosVersion(glversion, glProfile)); } else { return(new KhronosVersion(glversion, KhronosVersion.ProfileCompatibility)); } }
/// <summary> /// Query the version of the current OpenGL context. /// </summary> /// <returns> /// It returns the <see cref="KhronosVersion" /> specifying the actual version of the context current on this thread. /// </returns> public static KhronosVersion QueryContextVersion() { BindAPIFunction <Gl>("glGetError", Version100, null); BindAPIFunction <Gl>("glGetString", Version100, null); BindAPIFunction <Gl>("glGetIntegerv", Version100, null); // Parse version string (effective for detecting Desktop and ES contextes) string str = GetString(StringName.Version); KhronosVersion glVersion = KhronosVersion.Parse(str); // Context profile if (glVersion.Api == KhronosVersion.API_GL && glVersion >= Version320) { string glProfile; Get(CONTEXT_PROFILE_MASK, out int ctxProfile); if ((ctxProfile & CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0) { glProfile = KhronosVersion.PROFILE_COMPATIBILITY; } else if ((ctxProfile & CONTEXT_CORE_PROFILE_BIT) != 0) { glProfile = KhronosVersion.PROFILE_CORE; } else { glProfile = KhronosVersion.PROFILE_COMPATIBILITY; } return(new KhronosVersion(glVersion, glProfile)); } else { return(new KhronosVersion(glVersion, KhronosVersion.PROFILE_COMPATIBILITY)); } }
/// <summary> /// Link delegates fields using import declarations. /// </summary> /// <param name="path"> /// A <see cref="String"/> that specifies the assembly file path containing the import functions. /// </param> /// <param name="getAddress"> /// A <see cref="GetAddressDelegate"/> used for getting function pointers. This parameter is dependent on the currently running platform. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="path"/> or <paramref name="getAddress"/> is null. /// </exception> private static void BindAPI <T>(string path, GetAddressDelegate getAddress, KhronosVersion version, ExtensionsCollection extensions) { if (path == null) { throw new ArgumentNullException("path"); } if (getAddress == null) { throw new ArgumentNullException("getAddress"); } FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); if (functionContext == null) { throw new InvalidOperationException("unrecognized API type"); } foreach (FieldInfo fi in functionContext.Delegates) { BindAPIFunction(path, getAddress, functionContext, fi, version, extensions); } }
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (Object.ReferenceEquals(value, null)) { return(base.ConvertFrom(context, culture, value)); } Type valueType = value.GetType(); if (valueType == typeof(string)) { string valueString = (string)value; if (valueString == String.Empty) { return(null); } return(KhronosVersion.Parse(valueString)); } // Base implementation return(base.ConvertFrom(context, culture, value)); }
/// <summary> /// Query the supported extensions. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the version of the API context. /// </param> /// <param name="extensions"> /// An array of strings that specifies the supported extensions. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="extensions"/> is null. /// </exception> protected void Query(KhronosVersion version, string[] extensions) { if (version == null) { throw new ArgumentNullException("version"); } if (extensions == null) { throw new ArgumentNullException("extensions"); } // Cache extension names in registry _ExtensionsRegistry.Clear(); foreach (string extension in extensions) { if (!_ExtensionsRegistry.ContainsKey(extension)) { _ExtensionsRegistry.Add(extension, true); } } // Sync fields SyncMembers(version); }
/// <summary> /// Creates a context, specifying attributes. /// </summary> /// <param name="sharedContext"> /// A <see cref="IntPtr"/> that specify a context that will share objects with the returned one. If /// it is IntPtr.Zero, no sharing is performed. /// </param> /// <param name="attribsList"> /// A <see cref="T:Int32[]"/> that specifies the attributes list. /// </param> /// <param name="api"> /// A <see cref="KhronosVersion"/> that specifies the API to be implemented by the returned context. It can be null indicating the /// default API for this DeviceContext implementation. If it is possible, try to determine the API version also. /// </param> /// <returns> /// A <see cref="IntPtr"/> that represents the handle of the created context. If the context cannot be /// created, it returns IntPtr.Zero. /// </returns> /// <exception cref="ArgumentNullException"> /// Exception thrown if <see cref="attribsList"/> is null. /// </exception> /// <exception cref="ArgumentException"> /// Exception thrown if <paramref name="attribsList"/> length is zero or if the last item of <paramref name="attribsList"/> /// is not zero. /// </exception> public override IntPtr CreateContextAttrib(IntPtr sharedContext, int[] attribsList, KhronosVersion api) { if (api != null && api.Api != KhronosVersion.ApiGl) { List <int> adulteredAttribs = new List <int>(attribsList); // Support check switch (api.Api) { case KhronosVersion.ApiGles1: case KhronosVersion.ApiGles2: if (Glx.CurrentExtensions.CreateContextEsProfile_EXT == false) { throw new NotSupportedException("OpenGL ES API not supported"); } break; default: throw new NotSupportedException(String.Format("'{0}' API not supported", api.Api)); } // Remove trailing 0 if (adulteredAttribs.Count > 0 && adulteredAttribs[adulteredAttribs.Count - 1] == Gl.NONE) { adulteredAttribs.RemoveAt(adulteredAttribs.Count - 1); } // Add required attributes int majorVersionIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Gl.MAJOR_VERSION); }); int minorVersionIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Gl.MINOR_VERSION); }); int profileMaskIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Gl.CONTEXT_PROFILE_MASK); }); if (majorVersionIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.MAJOR_VERSION, api.Major }); majorVersionIndex = adulteredAttribs.Count - 2; } if (minorVersionIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.MINOR_VERSION, api.Minor }); minorVersionIndex = adulteredAttribs.Count - 2; } if (profileMaskIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.CONTEXT_PROFILE_MASK, 0 }); profileMaskIndex = adulteredAttribs.Count - 2; } switch (api.Api) { case KhronosVersion.ApiGles1: // Ignore API version: force always to 1.0 adulteredAttribs[majorVersionIndex + 1] = 1; adulteredAttribs[minorVersionIndex + 1] = 1; adulteredAttribs[profileMaskIndex + 1] |= (int)Glx.CONTEXT_ES_PROFILE_BIT_EXT; break; case KhronosVersion.ApiGles2: // Uses API version: it may be greater than 2.0(?) adulteredAttribs[majorVersionIndex + 1] = api.Major; adulteredAttribs[minorVersionIndex + 1] = api.Minor; adulteredAttribs[profileMaskIndex + 1] |= (int)Glx.CONTEXT_ES_PROFILE_BIT_EXT; break; default: throw new NotSupportedException(String.Format("'{0}' API not supported", api.Api)); } // Restore trailing 0 adulteredAttribs.Add(Gl.NONE); using (Glx.XLock displayLock = new Glx.XLock(Display)) { Debug.Assert(_FBConfig != IntPtr.Zero, "SetPixelFormat not executed"); return(Glx.CreateContextAttribsARB(Display, _FBConfig, sharedContext, true, adulteredAttribs.ToArray())); } } else { using (Glx.XLock displayLock = new Glx.XLock(Display)) { Debug.Assert(_FBConfig != IntPtr.Zero, "SetPixelFormat not executed"); return(Glx.CreateContextAttribsARB(Display, _FBConfig, sharedContext, true, attribsList)); } } }
/// <summary> /// Creates a context, specifying attributes. /// </summary> /// <param name="sharedContext"> /// A <see cref="IntPtr"/> that specify a context that will share objects with the returned one. If /// it is IntPtr.Zero, no sharing is performed. /// </param> /// <param name="attribsList"> /// A <see cref="T:Int32[]"/> that specifies the attributes list. /// </param> /// <param name="api"> /// A <see cref="KhronosVersion"/> that specifies the API to be implemented by the returned context. It can be null indicating the /// default API for this DeviceContext implementation. If it is possible, try to determine the API version also. /// </param> /// <returns> /// A <see cref="IntPtr"/> that represents the handle of the created context. If the context cannot be /// created, it returns IntPtr.Zero. /// </returns> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="attribsList"/> is null. /// </exception> /// <exception cref="ArgumentException"> /// Exception thrown if <paramref name="attribsList"/> length is zero or if the last item of <paramref name="attribsList"/> /// is not zero. /// </exception> public override IntPtr CreateContextAttrib(IntPtr sharedContext, int[] attribsList, KhronosVersion api) { if (attribsList == null) { throw new ArgumentNullException(nameof(attribsList)); } if (attribsList.Length == 0) { throw new ArgumentException("zero length array", nameof(attribsList)); } if (attribsList[attribsList.Length - 1] != Egl.NONE) { throw new ArgumentException("not EGL_NONE-terminated array", nameof(attribsList)); } IntPtr context; // Select surface pixel format automatically if (_NativeSurface != null && _NativeSurface.Handle != IntPtr.Zero) { int[] configId = new int[1]; if (Egl.QuerySurface(Display, EglSurface, Egl.CONFIG_ID, configId) == false) { throw new InvalidOperationException("unable to query EGL surface config ID"); } _Config = ChoosePixelFormat(Display, configId[0]); } // Bind API if (Version >= Egl.Version_120) { uint apiEnum; switch (api.Api) { case KhronosVersion.ApiGles2: case KhronosVersion.ApiGles1: case null: // Default apiEnum = Egl.OPENGL_ES_API; break; case KhronosVersion.ApiGl: apiEnum = Egl.OPENGL_API; break; case KhronosVersion.ApiVg: apiEnum = Egl.OPENVG_API; break; default: throw new InvalidOperationException($"'{api}' API not available"); } if (Egl.BindAPI(apiEnum) == false) { throw new InvalidOperationException("no ES API"); } } else if (api != null && api.Api != KhronosVersion.ApiGles2 && api.Api != KhronosVersion.ApiGles1) { throw new InvalidOperationException($"'{api}' API not available"); } // Create context if ((context = Egl.CreateContext(Display, _Config, sharedContext, attribsList)) == IntPtr.Zero) { throw new EglException(Egl.GetError()); } // Create native surface (pixel format pending) // @todo Back-buffer? if (_NativeSurface != null && _NativeSurface.Handle == IntPtr.Zero) { _NativeSurface.CreateHandle(_Config, new[] { Egl.NONE }); } return(context); }
/// <summary> /// Set the device pixel format. /// </summary> /// <param name="pixelFormat"> /// A <see cref="DevicePixelFormat"/> that specifies the pixel format to set. /// </param> private static IntPtr ChoosePixelFormat(IntPtr display, KhronosVersion version, DevicePixelFormat pixelFormat) { if (version == null) { throw new ArgumentNullException(nameof(version)); } if (pixelFormat == null) { throw new ArgumentNullException(nameof(pixelFormat)); } List <int> configAttribs = new List <int>(); int[] configCount = new int[1]; IntPtr[] configs = new IntPtr[8]; int surfaceType = 0; if (version >= Egl.Version_120) { configAttribs.AddRange(new[] { Egl.RENDERABLE_TYPE, Egl.OPENGL_ES2_BIT }); } if (pixelFormat.RenderWindow) { surfaceType |= Egl.WINDOW_BIT; } if (pixelFormat.RenderPBuffer) { surfaceType |= Egl.PBUFFER_BIT; } if (surfaceType != 0) { configAttribs.AddRange(new[] { Egl.SURFACE_TYPE, surfaceType }); } switch (pixelFormat.ColorBits) { case 24: configAttribs.AddRange(new[] { Egl.RED_SIZE, 8, Egl.GREEN_SIZE, 8, Egl.BLUE_SIZE, 8 }); break; case 32: configAttribs.AddRange(new[] { Egl.RED_SIZE, 8, Egl.GREEN_SIZE, 8, Egl.BLUE_SIZE, 8, Egl.ALPHA_SIZE, 8 }); break; default: configAttribs.AddRange(new[] { Egl.BUFFER_SIZE, pixelFormat.ColorBits }); break; } if (pixelFormat.DepthBits > 0) { configAttribs.AddRange(new[] { Egl.DEPTH_SIZE, pixelFormat.DepthBits }); } if (pixelFormat.StencilBits > 0) { configAttribs.AddRange(new[] { Egl.STENCIL_SIZE, pixelFormat.StencilBits }); } configAttribs.Add(Egl.NONE); if (Egl.ChooseConfig(display, configAttribs.ToArray(), configs, configs.Length, configCount) == false) { throw new InvalidOperationException("unable to choose configuration"); } if (configCount[0] == 0) { throw new InvalidOperationException("no available configuration"); } return(configs[0]); }
/// <summary> /// Bind a single OpenGL delegates to a specific API. /// </summary> /// <param name="version"> /// A <see cref="KhronosVersion"/> that specifies the API to bind. /// </param> /// <param name="extensions"> /// A <see cref="KhronosApi.ExtensionsCollection"/> that specifies the extensions supported. It can be null. /// </param> /// <param name="functionName"> /// A <see cref="String"/> that specifies the name of the function to bind. /// </param> internal static void BindAPIFunction(KhronosVersion version, ExtensionsCollection extensions, string functionName) { BindAPIFunction <Gl>(GetPlatformLibrary(version), functionName, GetProcAddressGLOS, version, extensions); }
/// <summary> /// Check whether commands implemented by the current driver have a corresponding extension not enabled by driver. /// </summary> public static void EnableExperimentalExtensions(KhronosVersion version, Extensions extensions) { CheckExtensionCommands <Gl>(version, extensions, true); }
/// <summary> /// Bind the OpenGL delegates for the API corresponding to the specified OpenGL API. /// </summary> /// <param name="version"> /// A <see cref="KhronosVersion"/> that specifies the API to bind. /// </param> /// <param name="extensions"> /// A <see cref="ExtensionsCollection"/> that specifies the extensions supported. It can be null. /// </param> public static void BindAPI(KhronosVersion version, ExtensionsCollection extensions) { BindAPI(version, extensions, GetProcAddress.GetProcAddressGL); }
/// <summary> /// Link delegates field using import declaration, using platform specific method for determining procedures address. /// </summary> internal static void BindAPIFunction <T>(string path, string functionName, KhronosVersion version, ExtensionsCollection extensions) { BindAPIFunction <T>(path, functionName, GetProcAddress.GetProcAddressOS, version, extensions); }
/// <summary> /// Query the OpenGL implementation limits. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion" /> that specifies the GL version. /// </param> /// <param name="glExtensions"> /// A <see cref="Gl.Extensions" /> that specify the supported OpenGL extension by the current /// implementation. /// </param> /// <returns> /// It returns a <see cref="Gl.Limits" /> that specify the current OpenGL implementation limits. /// </returns> /// <remarks> /// It is assumed to have a valid OpenGL context current on the calling thread. /// </remarks> public static Limits Query(KhronosVersion version, Extensions glExtensions) { if (glExtensions == null) { throw new ArgumentNullException("glExtensions"); } var graphicsLimits = new Limits(); IEnumerable <FieldInfo> graphicsLimitsFields = typeof(Limits).GetTypeInfo().DeclaredFields; // Supress errors. Some limits might be missing from certain drivers and versions. SuppressingErrors = true; foreach (FieldInfo field in graphicsLimitsFields) { var graphicsLimitAttribute = (LimitAttribute)field.GetCustomAttribute(typeof(LimitAttribute)); if (graphicsLimitAttribute == null) { continue; } // Check extension support Attribute[] graphicsExtensionAttributes = new List <Attribute>(field.GetCustomAttributes(typeof(RequiredByFeatureAttribute))).ToArray(); if (graphicsExtensionAttributes != null && graphicsExtensionAttributes.Length > 0) { bool supported = Array.Exists(graphicsExtensionAttributes, delegate(Attribute item) { var featureAttribute = (RequiredByFeatureAttribute)item; return(featureAttribute.IsSupported(version, glExtensions)); }); if (!supported) { continue; } } // Determine which method is used to get the OpenGL limit MethodInfo getMethod; if (field.FieldType != typeof(string)) { getMethod = typeof(Gl).GetMethod("Get", field.FieldType.IsArray ? new[] { typeof(int), field.FieldType } : new[] { typeof(int), field.FieldType.MakeByRefType() }); } else { getMethod = typeof(Gl).GetMethod("GetString", new[] { typeof(int) }); } if (getMethod == null) { Engine.Log.Error($"GraphicsLimits field " + field.Name + " doesn't have a OpenGL compatible type.", MessageSource.GL); continue; } if (field.FieldType != typeof(string)) { object obj = field.FieldType.IsArray == false ? Activator.CreateInstance(field.FieldType) : Array.CreateInstance(field.FieldType.GetElementType(), (int)graphicsLimitAttribute.ArrayLength); try { object[] @params = { graphicsLimitAttribute.EnumValue, obj }; getMethod.Invoke(null, @params); field.SetValue(graphicsLimits, @params[1]); } catch (TargetInvocationException exception) { Engine.Log.Error($"Getting {field.Name} (0x{graphicsLimitAttribute.EnumValue:X4}): {exception.InnerException?.Message}", MessageSource.GL); } } else { try { var s = (string)getMethod.Invoke(null, new object[] { graphicsLimitAttribute.EnumValue }); field.SetValue(graphicsLimits, s); } catch (TargetInvocationException exception) { Engine.Log.Error($"Getting {field.Name} (0x{graphicsLimitAttribute.EnumValue}): {exception.InnerException?.Message}", MessageSource.GL); } } } SuppressingErrors = false; return(graphicsLimits); }
/// <summary> /// Check whether commands implemented by the current driver have a corresponding extension declaring the /// support of them. /// </summary> /// <typeparam name="T"> /// The type of the KhronosApi to inspect for commands. /// </typeparam> /// <param name="version"> /// The <see cref="KhronosVersion"/> currently implemented by the current context on this thread. /// </param> /// <param name="extensions"> /// The <see cref="ExtensionsCollection"/> that specifies the extensions supported by the driver. /// </param> protected static void CheckExtensionCommands <T>(KhronosVersion version, ExtensionsCollection extensions, bool enableExtensions) where T : KhronosApi { if (version == null) { throw new ArgumentNullException("version"); } if (extensions == null) { throw new ArgumentNullException("extensions"); } #if !NETCORE Type apiType = typeof(T); FunctionContext functionContext = GetFunctionContext(apiType); Debug.Assert(functionContext != null); if (functionContext == null) { throw new InvalidOperationException("unrecognized API type"); } LogComment("Checking commands for {0}", version); Dictionary <string, List <Type> > hiddenVersions = new Dictionary <string, List <Type> >(); Dictionary <string, bool> hiddenExtensions = new Dictionary <string, bool>(); foreach (FieldInfo fi in functionContext.Delegates) { Delegate fiDelegateType = (Delegate)fi.GetValue(null); string commandName = fi.Name.Substring(3); bool commandDefined = fiDelegateType != null; bool supportedByFeature = false; Type delegateType = fi.DeclaringType.GetNestedType(fi.Name.Substring(1), BindingFlags.Public | BindingFlags.NonPublic); IEnumerable <object> requiredByFeatureAttributes = delegateType.GetCustomAttributes(typeof(RequiredByFeatureAttribute), false); foreach (RequiredByFeatureAttribute requiredByFeatureAttribute in requiredByFeatureAttributes) { supportedByFeature |= requiredByFeatureAttribute.IsSupported(version, extensions); } // Find the underlying extension RequiredByFeatureAttribute hiddenVersionAttrib = null; RequiredByFeatureAttribute hiddenExtensionAttrib = null; foreach (RequiredByFeatureAttribute requiredByFeatureAttribute in requiredByFeatureAttributes) { if (requiredByFeatureAttribute.IsSupportedApi(version.Api) == false) { // Version attribute if (hiddenVersionAttrib == null) { hiddenVersionAttrib = requiredByFeatureAttribute; } } else { // Extension attribute if (hiddenExtensionAttrib == null) { hiddenExtensionAttrib = requiredByFeatureAttribute; } } } if (commandDefined != supportedByFeature) { #if DEBUG_VERBOSE string supportString = "any feature"; if (hiddenVersionAttrib != null) { supportString = hiddenVersionAttrib.FeatureName; if (hiddenExtensionAttrib != null) { supportString += " or "; } } if (hiddenExtensionAttrib != null) { if (hiddenVersionAttrib == null) { supportString = String.Empty; } supportString += hiddenExtensionAttrib.FeatureName; } #endif if (commandDefined) { #if DEBUG_VERBOSE LogComment("The command {0} is defined, but {1} support is not advertised.", commandName, supportString); #endif if (hiddenVersionAttrib != null && hiddenExtensionAttrib == null) { List <Type> versionDelegates = new List <Type>(); if (hiddenVersions.TryGetValue(hiddenVersionAttrib.FeatureName, out versionDelegates) == false) { hiddenVersions.Add(hiddenVersionAttrib.FeatureName, versionDelegates = new List <Type>()); } versionDelegates.Add(delegateType); } if (hiddenExtensionAttrib != null) { // Eventually leave to false for incomplete extensions if (hiddenExtensions.ContainsKey(hiddenExtensionAttrib.FeatureName) == false) { hiddenExtensions.Add(hiddenExtensionAttrib.FeatureName, true); } } } else { #if DEBUG_VERBOSE LogComment("The command {0} is not defined, but required by some feature.", commandName); #endif } } // Partial extensions are not supported if (hiddenExtensionAttrib != null && commandDefined == false && hiddenExtensions.ContainsKey(hiddenExtensionAttrib.FeatureName)) { hiddenExtensions[hiddenExtensionAttrib.FeatureName] = false; } } if (hiddenExtensions.Count > 0) { LogComment("Found {0} experimental extensions:", hiddenExtensions.Count); foreach (KeyValuePair <string, bool> hiddenExtension in hiddenExtensions) { LogComment("- {0}: {1}", hiddenExtension.Key, hiddenExtension.Value ? "experimental" : "experimental (partial, unsupported)"); } } if (hiddenVersions.Count > 0) { LogComment("Found {0} experimental version commands:", hiddenVersions.Count); foreach (KeyValuePair <string, List <Type> > hiddenVersion in hiddenVersions) { LogComment("- {0}", hiddenVersion.Key); foreach (Type delegateType in hiddenVersion.Value) { LogComment(" > {0}", delegateType.Name); } } } if (enableExtensions) { bool sync = false; foreach (KeyValuePair <string, bool> hiddenExtension in hiddenExtensions) { if (hiddenExtension.Value == false) { continue; // Do not enable partial extension } extensions.EnableExtension(hiddenExtension.Key); sync = true; } if (sync) { extensions.SyncMembers(version); } } #endif }
/// <summary> /// Determine whether an API command is compatible with the specific API version and extensions registry. /// </summary> /// <param name="function"> /// A <see cref="FieldInfo"/> that specifies the command delegate to set. This argument make avail attributes useful /// to determine the actual support for this command. /// </param> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the API version. /// </param> /// <param name="extensions"> /// The <see cref="ExtensionsCollection"/> that specifies the API extensions registry. /// </param> /// <returns> /// It returns a <see cref="Boolean"/> that specifies whether <paramref name="function"/> is supported by the /// API having the version <paramref name="version"/> and the extensions registry <paramref name="extensions"/>. /// </returns> internal static bool IsCompatibleField(FieldInfo function, KhronosVersion version, ExtensionsCollection extensions) { if (function == null) { throw new ArgumentNullException("function"); } if (version == null) { throw new ArgumentNullException("version"); } #if !NETCORE Attribute[] attrRequired = Attribute.GetCustomAttributes(function, typeof(RequiredByFeatureAttribute)); #else Attribute[] attrRequired = new List <Attribute>(function.GetCustomAttributes(typeof(RequiredByFeatureAttribute))).ToArray(); #endif KhronosVersion maxRequiredVersion = null; bool isRequired = false, isRemoved = false; foreach (RequiredByFeatureAttribute attr in attrRequired) { // Check for API support if (attr.IsSupported(version, extensions) == false) { continue; } // Supported! isRequired |= true; // Keep track of the maximum API version supporting this command // Note: useful for resurrected commands after deprecation if (maxRequiredVersion == null || maxRequiredVersion < attr.FeatureVersion) { maxRequiredVersion = attr.FeatureVersion; } } if (isRequired) { // Note: indeed the feature could be supported; check whether it is removed #if !NETCORE Attribute[] attrRemoved = Attribute.GetCustomAttributes(function, typeof(RemovedByFeatureAttribute)); #else Attribute[] attrRemoved = new List <Attribute>(function.GetCustomAttributes(typeof(RemovedByFeatureAttribute))).ToArray(); #endif KhronosVersion maxRemovedVersion = null; foreach (RemovedByFeatureAttribute attr in attrRemoved) { if (attr.IsRemoved(version, extensions) == false) { continue; } // Removed! isRemoved |= true; // Keep track of the maximum API version removing this command if (maxRemovedVersion == null || maxRemovedVersion < attr.FeatureVersion) { maxRemovedVersion = attr.FeatureVersion; } } // Check for resurrection if (isRemoved) { Debug.Assert(maxRequiredVersion != null); Debug.Assert(maxRemovedVersion != null); if (maxRequiredVersion > maxRemovedVersion) { isRemoved = false; } } return(isRemoved == false); } else { return(false); } }
/// <summary> /// Link delegates fields using import declarations. /// </summary> /// <param name="path"> /// A <see cref="String"/> that specifies the assembly file path containing the import functions. /// </param> /// <param name="getAddress"> /// A <see cref="GetAddressDelegate"/> used for getting function pointers. This parameter is dependent on the currently running platform. /// </param> /// <param name="functionContext"> /// A <see cref="FunctionContext"/> mapping a <see cref="MethodInfo"/> with the relative function name. /// </param> /// <param name="function"> /// A <see cref="FieldInfo"/> that specifies the underlying function field to be updated. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="path"/>, <paramref name="function"/> or <paramref name="getAddress"/> is null. /// </exception> private static void BindAPIFunction(string path, GetAddressDelegate getAddress, FunctionContext functionContext, FieldInfo function, KhronosVersion version, ExtensionsCollection extensions) { if (path == null) { throw new ArgumentNullException("path"); } if (functionContext == null) { throw new ArgumentNullException("functionContext"); } if (function == null) { throw new ArgumentNullException("function"); } if (getAddress == null) { throw new ArgumentNullException("getAddress"); } if (version != null || extensions != null) { if (IsCompatibleField(function, version, extensions) == false) { function.SetValue(null, null); // Function not supported: reset return; } } string importName = function.Name.Substring(1); // Delegate name always prefixes with 'p' IntPtr importAddress = IntPtr.Zero; // Load command address importAddress = getAddress(path, importName); // Manages aliases (load external symbol) if (importAddress == IntPtr.Zero) { #if !NETCORE Attribute[] aliasOfAttributes = Attribute.GetCustomAttributes(function, typeof(AliasOfAttribute)); #else Attribute[] aliasOfAttributes = new List <Attribute>(function.GetCustomAttributes(typeof(AliasOfAttribute))).ToArray(); #endif for (int i = 1 /* Skip base name */; i < aliasOfAttributes.Length; i++) { AliasOfAttribute aliasOfAttribute = (AliasOfAttribute)aliasOfAttributes[i]; if ((importAddress = getAddress(path, aliasOfAttribute.SymbolName)) != IntPtr.Zero) { break; } } } if (importAddress != IntPtr.Zero) { Delegate delegatePtr; // Try to load external symbol if ((delegatePtr = Marshal.GetDelegateForFunctionPointer(importAddress, function.FieldType)) == null) { MethodInfo methodInfo; if (functionContext.Imports.TryGetValue(importName, out methodInfo) == true) { #if !NETCORE delegatePtr = Delegate.CreateDelegate(function.FieldType, methodInfo); #else delegatePtr = methodInfo.CreateDelegate(function.FieldType); #endif } } if (delegatePtr != null) { function.SetValue(null, delegatePtr); } } else { function.SetValue(null, null); // Function not implemented: reset } }
/// <summary> /// Construct a CoreExtensionAttribute specifying the version numbers. /// </summary> /// <param name="major"> /// A <see cref="Int32"/> that specifies that major version number. /// </param> /// <param name="minor"> /// A <see cref="Int32"/> that specifies that minor version number. /// </param> /// <param name="revision"> /// A <see cref="Int32"/> that specifies that revision version number. /// </param> /// <param name="api"> /// A <see cref="String"/> that specifies the API name. /// </param> /// <exception cref="ArgumentException"> /// Exception thrown if <paramref name="major"/> is less or equals to 0, or if <paramref name="minor"/> or /// <paramref name="revision"/> are less than 0. /// </exception> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="api"/> is null. /// </exception> public CoreExtensionAttribute(int major, int minor, int revision, string api) { Version = new KhronosVersion(major, minor, revision, api); }
/// <summary> /// Query the OpenGL implementation limits. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the GL version. /// </param> /// <param name="glExtensions"> /// A <see cref="Gl.Extensions"/> that specify the supported OpenGL extension by the current /// implementation. /// </param> /// <returns> /// It returns a <see cref="Gl.Limits"/> that specify the current OpenGL implementation limits. /// </returns> /// <remarks> /// It is assumed to have a valid OpenGL context current on the calling thread. /// </remarks> public static Limits Query(KhronosVersion version, Extensions glExtensions) { if (glExtensions == null) { throw new ArgumentNullException("glExtensions"); } LogComment("Query OpenGL implementation limits."); Limits graphicsLimits = new Limits(); #if NETSTANDARD1_1 || NETSTANDARD1_4 IEnumerable <FieldInfo> graphicsLimitsFields = typeof(Limits).GetTypeInfo().DeclaredFields; #else IEnumerable <FieldInfo> graphicsLimitsFields = typeof(Limits).GetFields(BindingFlags.Public | BindingFlags.Instance); #endif foreach (FieldInfo field in graphicsLimitsFields) { #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE LimitAttribute graphicsLimitAttribute = (LimitAttribute)field.GetCustomAttribute(typeof(LimitAttribute)); Attribute[] graphicsExtensionAttributes = new List <Attribute>(field.GetCustomAttributes(typeof(RequiredByFeatureAttribute))).ToArray(); #else LimitAttribute graphicsLimitAttribute = (LimitAttribute)Attribute.GetCustomAttribute(field, typeof(LimitAttribute)); Attribute[] graphicsExtensionAttributes = Attribute.GetCustomAttributes(field, typeof(RequiredByFeatureAttribute)); #endif MethodInfo getMethod; if (graphicsLimitAttribute == null) { continue; } // Check extension support if ((graphicsExtensionAttributes != null) && (graphicsExtensionAttributes.Length > 0)) { bool supported = Array.Exists(graphicsExtensionAttributes, delegate(Attribute item) { RequiredByFeatureAttribute featureAttribute = (RequiredByFeatureAttribute)item; return(featureAttribute.IsSupported(version, glExtensions)); }); if (supported == false) { continue; } } // Determine which method is used to get the OpenGL limit #if NETSTANDARD1_1 || NETSTANDARD1_4 if (field.FieldType != typeof(String)) { if (field.FieldType.IsArray == true) { getMethod = typeof(Gl).GetTypeInfo().GetDeclaredMethod("Get"); } else { getMethod = typeof(Gl).GetTypeInfo().GetDeclaredMethod("Get"); } } else { getMethod = typeof(Gl).GetTypeInfo().GetDeclaredMethod("GetString"); } #elif NETCORE if (field.FieldType != typeof(String)) { if (field.FieldType.IsArray == true) { getMethod = typeof(Gl).GetMethod("Get", new Type[] { typeof(Int32), field.FieldType }); } else { getMethod = typeof(Gl).GetMethod("Get", new Type[] { typeof(Int32), field.FieldType.MakeByRefType() }); } } else { getMethod = typeof(Gl).GetMethod("GetString", new Type[] { typeof(Int32) }); } #else if (field.FieldType != typeof(String)) { if (field.FieldType.IsArray == true) { getMethod = typeof(Gl).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(Int32), field.FieldType }, null); } else { getMethod = typeof(Gl).GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(Int32), field.FieldType.MakeByRefType() }, null); } } else { getMethod = typeof(Gl).GetMethod("GetString", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(Int32) }, null); } #endif if (getMethod != null) { if (field.FieldType != typeof(String)) { object obj; if (field.FieldType.IsArray == false) { obj = Activator.CreateInstance(field.FieldType); } else { obj = Array.CreateInstance(field.FieldType.GetElementType(), (int)graphicsLimitAttribute.ArrayLength); } try { object[] @params = new object[] { graphicsLimitAttribute.EnumValue, obj }; getMethod.Invoke(null, @params); field.SetValue(graphicsLimits, @params[1]); } catch (GlException) { } catch (Exception) { } } else { try { string s = (string)getMethod.Invoke(null, new object[] { graphicsLimitAttribute.EnumValue }); field.SetValue(graphicsLimits, s); } catch (GlException) { } catch (Exception) { } } } else { throw new InvalidOperationException("GraphicsLimits field " + field.Name + " doesn't have a OpenGL compatible type"); } } return(graphicsLimits); }
/// <summary> /// Query the supported extensions. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the version of the API context. /// </param> /// <param name="extensions"> /// An array of strings that specifies the supported extensions. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="extensions"/> is null. /// </exception> protected void Query(KhronosVersion version, string[] extensions) { if (version == null) { throw new ArgumentNullException("version"); } if (extensions == null) { throw new ArgumentNullException("extensions"); } // Cache extension names in registry _ExtensionsRegistry.Clear(); foreach (string extension in extensions) { if (!_ExtensionsRegistry.ContainsKey(extension)) { _ExtensionsRegistry.Add(extension, true); } } // Set all extension fields Type thisType = GetType(); foreach (FieldInfo fieldInfo in thisType.GetFields(BindingFlags.Instance | BindingFlags.Public)) { // Check boolean field (defensive) Debug.Assert(fieldInfo.FieldType == typeof(bool)); if (fieldInfo.FieldType != typeof(bool)) { continue; } bool support = false; // Support by extension Attribute[] coreAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(CoreExtensionAttribute)); if ((coreAttributes != null) && (coreAttributes.Length > 0)) { foreach (CoreExtensionAttribute coreAttribute in coreAttributes) { if (version.Api == coreAttribute.Version.Api && version >= coreAttribute.Version) { support |= true; break; } } } // Support by extension Attribute[] extensionAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(ExtensionAttribute)); if ((extensionAttributes != null) && (extensionAttributes.Length > 0)) { foreach (ExtensionAttribute extensionAttribute in extensionAttributes) { if (_ExtensionsRegistry.ContainsKey(extensionAttribute.ExtensionName)) { support |= true; break; } } } fieldInfo.SetValue(this, support); } }
/// <summary> /// Creates a context, specifying attributes. /// </summary> /// <param name="sharedContext"> /// A <see cref="IntPtr"/> that specify a context that will share objects with the returned one. If /// it is IntPtr.Zero, no sharing is performed. /// </param> /// <param name="attribsList"> /// A <see cref="T:Int32[]"/> that specifies the attributes list. /// </param> /// <param name="api"> /// A <see cref="KhronosVersion"/> that specifies the API to be implemented by the returned context. It can be null indicating the /// default API for this DeviceContext implementation. If it is possible, try to determine the API version also. /// </param> /// <returns> /// A <see cref="IntPtr"/> that represents the handle of the created context. If the context cannot be /// created, it returns IntPtr.Zero. /// </returns> /// <exception cref="ArgumentNullException"> /// Exception thrown if <see cref="attribsList"/> is null. /// </exception> /// <exception cref="ArgumentException"> /// Exception thrown if <paramref name="attribsList"/> length is zero or if the last item of <paramref name="attribsList"/> /// is not zero. /// </exception> public override IntPtr CreateContextAttrib(IntPtr sharedContext, int[] attribsList, KhronosVersion api) { if ((attribsList != null) && (attribsList.Length == 0)) { throw new ArgumentException("zero length array", "attribsList"); } if ((attribsList != null) && (attribsList[attribsList.Length - 1] != 0)) { throw new ArgumentException("not zero-terminated array", "attribsList"); } if (api != null && api.Api != KhronosVersion.ApiGl) { List <int> adulteredAttribs = new List <int>(attribsList); // Support check switch (api.Api) { case KhronosVersion.ApiGles1: case KhronosVersion.ApiGles2: if (Wgl.CurrentExtensions.CreateContextEsProfile_EXT == false) { throw new NotSupportedException("OpenGL ES API not supported"); } break; default: throw new NotSupportedException(String.Format("'{0}' API not supported", api.Api)); } // Remove trailing 0 if (adulteredAttribs.Count > 0 && adulteredAttribs[adulteredAttribs.Count - 1] == Gl.NONE) { adulteredAttribs.RemoveAt(adulteredAttribs.Count - 1); } // Add required attributes int majorVersionIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Wgl.CONTEXT_MAJOR_VERSION_ARB); }); int minorVersionIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Wgl.CONTEXT_MINOR_VERSION_ARB); }); int profileMaskIndex = adulteredAttribs.FindIndex(delegate(int item) { return(item == Wgl.CONTEXT_PROFILE_MASK_ARB); }); if (majorVersionIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.MAJOR_VERSION, api.Major }); majorVersionIndex = adulteredAttribs.Count - 2; } if (minorVersionIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.MINOR_VERSION, api.Minor }); minorVersionIndex = adulteredAttribs.Count - 2; } if (profileMaskIndex < 0) { adulteredAttribs.AddRange(new int[] { Gl.CONTEXT_PROFILE_MASK, 0 }); profileMaskIndex = adulteredAttribs.Count - 2; } switch (api.Api) { case KhronosVersion.ApiGles1: // Ignore API version: force always to 1.0 adulteredAttribs[majorVersionIndex + 1] = 1; adulteredAttribs[minorVersionIndex + 1] = 0; adulteredAttribs[profileMaskIndex + 1] |= (int)Wgl.CONTEXT_ES_PROFILE_BIT_EXT; break; case KhronosVersion.ApiGles2: // Uses API version: it may be greater than 2.0(?) adulteredAttribs[majorVersionIndex + 1] = 2; adulteredAttribs[minorVersionIndex + 1] = 0; adulteredAttribs[profileMaskIndex + 1] |= (int)Wgl.CONTEXT_ES_PROFILE_BIT_EXT; break; default: Debug.Fail("API not implemented"); throw new NotSupportedException(String.Format("'{0}' API not supported", api.Api)); } // Restore trailing 0 adulteredAttribs.Add(Gl.NONE); return(Wgl.CreateContextAttribsARB(_DeviceContext, sharedContext, adulteredAttribs.ToArray())); } else { return(Wgl.CreateContextAttribsARB(_DeviceContext, sharedContext, attribsList)); } }
/// <summary> /// Link delegates fields using import declarations, using platform specific method for determining procedures addresses. /// </summary> /// <param name="imports"> /// A <see cref="ImportMap"/> mapping a <see cref="MethodInfo"/> with the relative function name. /// </param> /// <param name="delegates"> /// A <see cref="DelegateList"/> listing <see cref="FieldInfo"/> related to function delegates. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="imports"/> or <paramref name="delegates"/> is null. /// </exception> internal static void BindAPI <T>(string path, IGetProcAddress getProcAddress, KhronosVersion version, ExtensionsCollection extensions) { BindAPI <T>(path, delegate(string libpath, string function) { // Note: IGetProcAddress implementation may have GetOpenGLProcAddress equivalent to GetProcAddress IntPtr procAddress = getProcAddress.GetOpenGLProcAddress(function); if (procAddress == IntPtr.Zero) { return(GetProcAddress.GetProcAddressOS.GetProcAddress(libpath, function)); } if (procAddress == IntPtr.Zero) { LogComment("Warning: no address for command {0}.", function); } return(procAddress); }, version, extensions); }
/// <summary> /// Initialize OpenGL namespace static environment. This method shall be called before any other classes methods. /// </summary> public static void Initialize() { if (_Initialized == true) { return; // Already initialized } _Initialized = true; // Before linking procedures, append ANGLE directory in path string assemblyPath = GetAssemblyLocation(); string anglePath = null; switch (Platform.CurrentPlatformId) { case Platform.Id.WindowsNT: if (assemblyPath != null) { #if DEBUG if (IntPtr.Size == 8) { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10d\x64"); } else { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10d\x86"); } #else if (IntPtr.Size == 8) { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10\x64"); } else { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10\x86"); } #endif } break; case Platform.Id.Linux: // Note: on RPi libEGL.so depends on libGLESv2.so, so it's required to pre-load the shared library // Note: maybe a configurable and generic method for pre-loading assemblies may be introduced GetProcAddressLinux.GetLibraryHandle("libGLESv2.so", false); break; } // Include ANGLE path, if any #if NETSTANDARD1_1 if (anglePath != String.Empty) { OpenGL.GetProcAddressOS.AddLibraryDirectory(Path.Combine(assemblyPath, anglePath)); } #else if (anglePath != null && Directory.Exists(anglePath)) { OpenGL.GetProcAddressOS.AddLibraryDirectory(Path.Combine(assemblyPath, anglePath)); } #endif // Load procedures BindAPI(); if (IsAvailable == false) { return; } #if DEBUG string envEglInit = Environment.GetEnvironmentVariable("EGL_INIT"); if (envEglInit != null && envEglInit == "NO") { return; } #endif // Platform initialization EglEventArgs args = new EglEventArgs(); RaiseEglInitializing(args); // Get EGL information IntPtr eglDisplay = Egl.GetDisplay(args.Display); try { if (Initialize(eglDisplay, null, null) == false) { throw new InvalidOperationException("unable to initialize EGL"); } // Query EGL version string eglVersionString = QueryString(eglDisplay, VERSION); _CurrentVersion = KhronosVersion.Parse(eglVersionString, KhronosVersion.ApiEgl); // Query EGL vendor _Vendor = QueryString(eglDisplay, VENDOR); // Client APIs List <string> clientApis = new List <string>(); if (_CurrentVersion >= Version_120) { string clientApisString = QueryString(eglDisplay, CLIENT_APIS); string[] clientApiTokens = System.Text.RegularExpressions.Regex.Split(clientApisString, " "); foreach (string api in DeviceContextEGL.ConvertApiNames(clientApiTokens)) { clientApis.Add(api); } } _AvailableApis = clientApis.ToArray(); // Null device context for querying extensions using (DeviceContextEGL deviceContext = new DeviceContextEGL(args.Display, IntPtr.Zero)) { _CurrentExtensions = new Extensions(); _CurrentExtensions.Query(deviceContext); } } finally { Terminate(eglDisplay); } }
/// <summary> /// Initialize OpenGL namespace static environment. This method shall be called before any other classes methods. /// </summary> public static void Initialize() { if (_Initialized == true) { return; // Already initialized } _Initialized = true; #if !NETSTANDARD1_1 // Optional initialization string envGlInit = Environment.GetEnvironmentVariable("OPENGL_NET_INIT"); if (envGlInit != null && envGlInit == "NO") { return; } #endif // Environment options LogComment("OpenGL.Net is initializing"); // Loader function OS API GL API // ------------------------------------------------------ // Supported platform: Windows // wglGetProcAddress WGL GL // wglGetProcAddress WGL GLES2+ (with WGL_create_context_es(2)?_profile_EXT) // eglGetProcAddress EGL(Angle) GLES2+ // ------------------------------------------------------ // Supported platform: Linux // glXGetProcAddress GLX GL // glXGetProcAddress GLX GLES2+ (with GLX_create_context_es(2)?_profile_EXT) // eglGetProcAddress EGL GLES2+ // ------------------------------------------------------ // Supported platform: Android // eglGetProcAddress EGL GL // eglGetProcAddress EGL GLES2+ try { #if !MONODROID // Determine whether use EGL as device context backend if (Egl.IsAvailable) { switch (Platform.CurrentPlatformId) { case Platform.Id.Linux: if (Glx.IsAvailable == false) { Egl.IsRequired = true; } break; } } #endif // Create native window for getting preliminary information on desktop systems // This instance will be used for creating contexts without explictly specify a window _NativeWindow = DeviceContext.CreateHiddenWindow(); // Create device context using (DeviceContext windowDevice = DeviceContext.Create()) { // Create basic OpenGL context IntPtr renderContext = windowDevice.CreateSimpleContext(); if (renderContext == IntPtr.Zero) { throw new NotImplementedException("unable to create a simple context"); } // Make contect current if (windowDevice.MakeCurrent(renderContext) == false) { throw new InvalidOperationException("unable to make current", windowDevice.GetPlatformException()); } #if !MONODROID // Reload platform function pointers, if required if (Egl.IsRequired == false) { switch (Platform.CurrentPlatformId) { case Platform.Id.WindowsNT: Wgl.BindAPI(); break; } } #endif // Query OpenGL informations string glVersion = GetString(StringName.Version); _CurrentVersion = KhronosVersion.Parse(glVersion); // Query OpenGL extensions (current OpenGL implementation, CurrentCaps) _CurrentExtensions = new Extensions(); _CurrentExtensions.Query(); // Query platform extensions windowDevice.QueryPlatformExtensions(); // Query OpenGL limits _CurrentLimits = Limits.Query(Gl.CurrentVersion, _CurrentExtensions); // Obtain current OpenGL Shading Language version string glslVersion = null; switch (_CurrentVersion.Api) { case KhronosVersion.ApiGl: if (_CurrentVersion >= Version_200 || _CurrentExtensions.ShadingLanguage100_ARB) { glslVersion = GetString(StringName.ShadingLanguageVersion); } break; case KhronosVersion.ApiGles2: glslVersion = GetString(StringName.ShadingLanguageVersion); break; } if (glslVersion != null) { _CurrentShadingVersion = GlslVersion.Parse(glslVersion, _CurrentVersion.Api); } // Vendor/Render information _Vendor = GetString(StringName.Vendor); _Renderer = GetString(StringName.Renderer); if (EnvDebug || EnvExperimental) { Debug.Assert(CurrentVersion != null && CurrentExtensions != null); CheckExtensionCommands <Gl>(CurrentVersion, CurrentExtensions, EnvExperimental); } // Before deletion, make uncurrent windowDevice.MakeCurrent(IntPtr.Zero); // Detroy context if (windowDevice.DeleteContext(renderContext) == false) { throw new InvalidOperationException("unable to delete OpenGL context"); } } LogComment("OpenGL.Net has been initialized"); } catch (Exception excepton) { _InitializationException = excepton; LogComment("Unable to initialize OpenGL.Net: {0}", _InitializationException.ToString()); } }
/// <summary> /// Link delegates field using import declaration, using platform specific method for determining procedures address. /// </summary> internal static void BindAPIFunction <T>(string path, string functionName, IGetProcAddress getProcAddress, KhronosVersion version, ExtensionsCollection extensions) { if (path == null) { throw new ArgumentNullException("path"); } if (functionName == null) { throw new ArgumentNullException("function"); } if (getProcAddress == null) { throw new ArgumentNullException("getAddress"); } FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); if (functionContext == null) { throw new InvalidOperationException("unrecognized API type"); } Type delegatesClass = typeof(T).GetNestedType("Delegates", BindingFlags.Static | BindingFlags.NonPublic); Debug.Assert(delegatesClass != null); if (delegatesClass == null) { throw new NotImplementedException("missing Delegates class"); } FieldInfo functionField = delegatesClass.GetField("p" + functionName, BindingFlags.Static | BindingFlags.NonPublic); Debug.Assert(functionField != null); if (functionField == null) { throw new NotImplementedException(String.Format("unable to find function named {0}", functionName)); } BindAPIFunction(path, delegate(string libpath, string function) { // Note: IGetProcAddress implementation may have GetOpenGLProcAddress equivalent to GetProcAddress IntPtr procAddress = getProcAddress.GetOpenGLProcAddress(function); if (procAddress == IntPtr.Zero) { return(GetProcAddress.GetProcAddressOS.GetProcAddress(libpath, function)); } return(procAddress); }, functionContext, functionField, version, extensions); }
/// <summary> /// Creates a context, specifying attributes. /// </summary> /// <param name="sharedContext"> /// A <see cref="IntPtr"/> that specify a context that will share objects with the returned one. If /// it is IntPtr.Zero, no sharing is performed. /// </param> /// <param name="attribsList"> /// A <see cref="T:Int32[]"/> that specifies the attributes list. The attribute list is dependent on the actual /// platform and the GL version and extension support. /// </param> /// <param name="api"> /// A <see cref="KhronosVersion"/> that specifies the API to be implemented by the returned context. It can be null indicating the /// default API for this DeviceContext implementation. If it is possible, try to determine the API version also. /// </param> /// <returns> /// A <see cref="IntPtr"/> that represents the handle of the created context. If the context cannot be /// created, it returns IntPtr.Zero. /// </returns> /// <exception cref="ArgumentNullException"> /// Exception thrown if <see cref="attribsList"/> is null. /// </exception> /// <exception cref="ArgumentException"> /// Exception thrown if <paramref name="attribsList"/> length is zero or if the last item of <paramref name="attribsList"/> /// is not zero. /// </exception> public abstract IntPtr CreateContextAttrib(IntPtr sharedContext, int[] attribsList, KhronosVersion api);
/// <summary> /// Initialize OpenGL namespace static environment. This method shall be called before any other classes methods. /// </summary> public static void Initialize() { if (_Initialized == true) { return; // Already initialized } _Initialized = true; // Before linking procedures, append ANGLE directory in path string assemblyPath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Egl)).Location); string anglePath = null; switch (Platform.CurrentPlatformId) { case Platform.Id.WindowsNT: #if DEBUG if (IntPtr.Size == 8) { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10d\x64"); } else { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10d\x86"); } #else if (IntPtr.Size == 8) { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10\x64"); } else { anglePath = Path.Combine(assemblyPath, @"ANGLE\winrt10\x86"); } #endif break; case Platform.Id.Linux: // Note: on RPi libEGL.so depends on libGLESv2.so, so it's required to pre-load the shared library GetProcAddressX11.GetLibraryHandle("libGLESv2.so", false); break; } // Include ANGLE path, if any if (anglePath != null && Directory.Exists(anglePath)) { OpenGL.GetProcAddress.GetProcAddressOS.AddLibraryDirectory(Path.Combine(assemblyPath, anglePath)); } // Load procedures string platformLibrary = GetPlatformLibrary(); try { LogComment("Querying EGL from {0}", platformLibrary); BindAPI <Egl>(platformLibrary, OpenGL.GetProcAddress.GetProcAddressOS); LogComment("EGL availability: {0}", IsAvailable); } catch (Exception exception) { /* Fail-safe (it may fail due Egl access) */ LogComment("EGL not available:\n{0}", exception.ToString()); } if (IsAvailable == false) { return; } #if DEBUG string envEglInit = Environment.GetEnvironmentVariable("EGL_INIT"); if (envEglInit != null && envEglInit == "NO") { return; } #endif // Platform initialization EglEventArgs args = new EglEventArgs(); RaiseEglInitializing(args); // Get EGL information IntPtr eglDisplay = Egl.GetDisplay(args.Display); try { if (Initialize(eglDisplay, null, null) == false) { throw new InvalidOperationException("unable to initialize EGL"); } // Query EGL version string eglVersionString = QueryString(eglDisplay, VERSION); _CurrentVersion = KhronosVersion.Parse(eglVersionString, KhronosVersion.ApiEgl); // Query EGL vendor _Vendor = QueryString(eglDisplay, VENDOR); // Client APIs if (_CurrentVersion >= Version_120) { string clientApisString = QueryString(eglDisplay, CLIENT_APIS); _AvailableApis = System.Text.RegularExpressions.Regex.Split(clientApisString, " "); } } finally { Terminate(eglDisplay); } }