/// <summary> /// Bind the 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> public static void BindAPI(KhronosVersion version, ExtensionsCollection extensions) { if (version == null) { throw new ArgumentNullException("version"); } BindAPI <Gl>(GetPlatformLibrary(version), GetProcAddressGLOS, version, extensions); }
/// <summary> /// Bind the 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="Khronos.KhronosApi.ExtensionsCollection"/> that specifies the extensions supported. It can be null. /// </param> public static void BindAPI(KhronosVersion version, ExtensionsCollection extensions) { if (version == null) { throw new ArgumentNullException(nameof(version)); } BindAPI <Gl>(version, extensions); }
/// <summary> /// Bind the 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="Khronos.KhronosApi.ExtensionsCollection"/> that specifies the extensions supported. It can be null. /// </param> public static void BindAPI(KhronosVersion version, ExtensionsCollection extensions) { if (version == null) { throw new ArgumentNullException(nameof(version)); } BindAPI <Gl>(GetPlatformLibrary(version), _GetAddressDelegate, version, extensions); }
/// <summary> /// Link delegates fields using import declarations. /// </summary> /// <param name="version"></param> /// <param name="extensions"></param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="path"/> or <paramref name="getAddress"/> is null. /// </exception> internal static void BindAPI <T>(KhronosVersion version, ExtensionsCollection extensions) { FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); foreach (FieldInfo fi in functionContext.Delegates) { BindAPIFunction(fi, version, extensions); } }
/// <summary> /// Bind the OpenGL delegates to a specific API. /// </summary> /// <param name="version"> /// A <see cref="KhronosVersion"/> that specifies the API to bind. /// </param> /// <param name="getProcAddress"> /// The <see cref="IGetProcAddress"/> used for loading function pointers. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="version"/> or <paramref name="getProcAddress"/> is null. /// </exception> private static void BindAPI(KhronosVersion version, ExtensionsCollection extensions, IGetProcAddress getProcAddress) { if (version == null) { throw new ArgumentNullException("version"); } if (getProcAddress == null) { throw new ArgumentNullException("getProcAddress"); } BindAPI <Gl>(GetPlatformLibrary(version), getProcAddress, version, extensions); }
/// <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> /// 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 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> /// 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); } }
/// <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. /// </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"); } RequiredByFeatureAttribute requiredByFeature = null; List <RequiredByFeatureAttribute> requiredByExtensions = new List <RequiredByFeatureAttribute>(); string defaultName = function.Name.Substring(1); // Delegate name always prefixes with 'p' if (version != null || extensions != null) { bool isRemoved = false; #region Check Requirement #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE Attribute[] attrRequired = new List <Attribute>(function.GetCustomAttributes(typeof(RequiredByFeatureAttribute))).ToArray(); #else Attribute[] attrRequired = Attribute.GetCustomAttributes(function, typeof(RequiredByFeatureAttribute)); #endif foreach (RequiredByFeatureAttribute attr in attrRequired) { // Check for API support if (attr.IsSupported(version, extensions) == false) { continue; } // Keep track of the features requiring this command if (attr.FeatureVersion != null) { // Version feature: keep track only of the maximum version if (requiredByFeature == null || requiredByFeature.FeatureVersion < attr.FeatureVersion) { requiredByFeature = attr; } } else { // Extension feature: collect every supporting extension requiredByExtensions.Add(attr); } } #endregion #region Check Deprecation/Removal if (requiredByFeature != null) { // Note: indeed the feature could be supported; check whether it is removed; this is checked only if // a non-extension feature is detected: extensions cannot remove commands #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE Attribute[] attrRemoved = new List <Attribute>(function.GetCustomAttributes(typeof(RemovedByFeatureAttribute))).ToArray(); #else Attribute[] attrRemoved = Attribute.GetCustomAttributes(function, typeof(RemovedByFeatureAttribute)); #endif KhronosVersion maxRemovedVersion = null; foreach (RemovedByFeatureAttribute attr in attrRemoved) { // Check for API support 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(requiredByFeature != null); Debug.Assert(maxRemovedVersion != null); if (requiredByFeature.FeatureVersion > maxRemovedVersion) { isRemoved = false; } } } #endregion // Do not check feature requirements in case of removal. Note: extensions are checked all the same if (isRemoved) { requiredByFeature = null; } } // Load function pointer IntPtr importAddress; if (requiredByFeature != null || version == null) { // Load command address (version feature) string functionName = defaultName; if (requiredByFeature != null && requiredByFeature.EntryPoint != null) { functionName = requiredByFeature.EntryPoint; } if ((importAddress = getAddress(path, functionName)) != IntPtr.Zero) { BindAPIFunction(function, importAddress); return; } } // Load command address (extension features) foreach (RequiredByFeatureAttribute extensionFeature in requiredByExtensions) { string functionName = extensionFeature.EntryPoint ?? defaultName; if ((importAddress = getAddress(path, functionName)) != IntPtr.Zero) { BindAPIFunction(function, importAddress); return; } } // Function not implemented: reset function.SetValue(null, null); }
/// <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> /// Link delegates field using import declaration, using platform specific method for determining procedures address. /// </summary> internal static void BindAPIFunction <T>(string functionName, KhronosVersion version, ExtensionsCollection extensions) { FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); BindAPIFunction(functionContext.GetFunction(functionName), version, extensions); }
/// <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> /// 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="bool" /> 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) { Debug.Assert(function != null); Debug.Assert(version != null); IEnumerable <Attribute> attrRequired = Attribute.GetCustomAttributes(function, typeof(RequiredByFeatureAttribute)); KhronosVersion maxRequiredVersion = null; bool required = false, removed = false; // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (RequiredByFeatureAttribute attr in attrRequired) { // Check for API support if (attr.IsSupported(version, extensions) == false) { continue; } // Supported! required = 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 (required) { // Note: indeed the feature could be supported; check whether it is removed IEnumerable <Attribute> attrRemoved = Attribute.GetCustomAttributes(function, typeof(RemovedByFeatureAttribute)); KhronosVersion maxRemovedVersion = null; // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (RemovedByFeatureAttribute attr in attrRemoved) { if (attr.IsRemoved(version, extensions) == false) { continue; } // Removed! removed = true; // Keep track of the maximum API version removing this command if (maxRemovedVersion == null || maxRemovedVersion < attr.FeatureVersion) { maxRemovedVersion = attr.FeatureVersion; } } // Check for resurrection if (removed) { Debug.Assert(maxRequiredVersion != null); Debug.Assert(maxRemovedVersion != null); if (maxRequiredVersion > maxRemovedVersion) { removed = false; } } return(removed == false); } return(false); }
public static void BindAPIFunction(string functionName, KhronosVersion version, ExtensionsCollection extensions, GraphicsContext context) { BindAPIFunction(_functionContext.GetFunction(functionName), version, extensions, context); }
/// <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(nameof(version)); } if (extensions == null) { throw new ArgumentNullException(nameof(extensions)); } #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE throw new NotImplementedException(); #else FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); LogComment($"Checking commands for {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); bool commandDefined = fiDelegateType != null; bool supportedByFeature = false; #if DEBUG_VERBOSE string commandName = fi.Name.Substring(3); #endif // Get the delegate type Type delegateType = fi.DeclaringType?.GetNestedType(fi.Name.Substring(1), BindingFlags.Public | BindingFlags.NonPublic); if (delegateType == null) { continue; // Support fields names not in sync with delegate types } // TODO Why not use 'fi' directly for getting attributes? They should be in sync 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)) { // 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; if (!hiddenVersions.TryGetValue(hiddenVersionAttrib.FeatureName, out versionDelegates)) { 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)) { 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 && hiddenExtensions.ContainsKey(hiddenExtensionAttrib.FeatureName)) { hiddenExtensions[hiddenExtensionAttrib.FeatureName] = false; } } if (hiddenExtensions.Count > 0) { LogComment($"Found {hiddenExtensions.Count} experimental extensions:"); foreach (KeyValuePair <string, bool> hiddenExtension in hiddenExtensions) { LogComment(string.Format($"- {0}: {1}", hiddenExtension.Key, hiddenExtension.Value ? "experimental" : "experimental (partial, unsupported)")); } } if (hiddenVersions.Count > 0) { LogComment($"Found {hiddenVersions.Count} experimental version commands:"); foreach (KeyValuePair <string, List <Type> > hiddenVersion in hiddenVersions) { LogComment($"- {hiddenVersion.Key}"); foreach (Type delegateType in hiddenVersion.Value) { LogComment($" > {delegateType.Name}"); } } } if (enableExtensions) { bool sync = false; foreach (KeyValuePair <string, bool> hiddenExtension in hiddenExtensions) { if (!hiddenExtension.Value) { continue; // Do not enable partial extension } extensions.EnableExtension(hiddenExtension.Key); sync = true; } if (sync) { extensions.SyncMembers(version); } } #endif }
/// <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. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="path"/> or <paramref name="getAddress"/> is null. /// </exception> internal static void BindAPI <T>(string path, GetAddressDelegate getAddress, KhronosVersion version, ExtensionsCollection extensions) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (getAddress == null) { throw new ArgumentNullException(nameof(getAddress)); } FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); foreach (FieldInfo fi in functionContext.Delegates) { BindAPIFunction(path, getAddress, fi, version, extensions); } }
/// <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, GetAddressDelegate getProcAddress, KhronosVersion version, ExtensionsCollection extensions) { FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); BindAPIFunction(path, getProcAddress, functionContext.GetFunction(functionName), version, extensions); }
/// <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> /// 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="Khronos.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>(null, functionName, version, extensions); }
public static void CheckExtensionCommands(KhronosVersion version, ExtensionsCollection extensions, bool enableExtensions) { CheckExtensionCommands <TestApi>(version, extensions, enableExtensions); }
/// <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); }
public static void BindAPIFunction(FieldInfo function, KhronosVersion version, ExtensionsCollection extensions, GraphicsContext context) { Debug.Assert(function != null); RequiredByFeatureAttribute requiredByFeature = null; var requiredByExtensions = new List <RequiredByFeatureAttribute>(); string defaultName = function.Name.Substring(1); // Delegate name always prefixes with 'p' if (version != null || extensions != null) { var removed = false; #region Check Requirement IEnumerable <Attribute> attrRequired = new List <Attribute>(function.GetCustomAttributes(typeof(RequiredByFeatureAttribute))); // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (RequiredByFeatureAttribute attr in attrRequired) { // Check for API support if (attr.IsSupported(version, extensions) == false) { continue; } // Keep track of the features requiring this command if (attr.FeatureVersion != null) { // Version feature: keep track only of the maximum version if (requiredByFeature == null || requiredByFeature.FeatureVersion < attr.FeatureVersion) { requiredByFeature = attr; } } else { // Extension feature: collect every supporting extension requiredByExtensions.Add(attr); } } #endregion #region Check Deprecation/Removal if (requiredByFeature != null) { // Note: indeed the feature could be supported; check whether it is removed; this is checked only if // a non-extension feature is detected: extensions cannot remove commands Attribute[] attrRemoved = Attribute.GetCustomAttributes(function, typeof(RemovedByFeatureAttribute)); KhronosVersion maxRemovedVersion = null; // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (RemovedByFeatureAttribute attr in attrRemoved) { // Check for API support if (attr.IsRemoved(version, extensions) == false) { continue; } // Removed! removed = true; // Keep track of the maximum API version removing this command if (maxRemovedVersion == null || maxRemovedVersion < attr.FeatureVersion) { maxRemovedVersion = attr.FeatureVersion; } } // Check for resurrection if (removed) { Debug.Assert(requiredByFeature != null); Debug.Assert(maxRemovedVersion != null); if (requiredByFeature.FeatureVersion > maxRemovedVersion) { removed = false; } } } #endregion // Do not check feature requirements in case of removal. Note: extensions are checked all the same if (removed) { requiredByFeature = null; } } // Load function pointer IntPtr importAddress; Delegate importDelegate; if (requiredByFeature != null || version == null) { // Load command address (version feature) string functionName = defaultName; if (requiredByFeature?.EntryPoint != null) { functionName = requiredByFeature.EntryPoint; } if (context.Native) { if ((importAddress = context.GetProcAddress(functionName)) != IntPtr.Zero) { BindNativeAPIFunction(function, importAddress); return; } } else { if ((importDelegate = context.GetProcAddressNonNative(functionName)) != null) { function.SetValue(null, importDelegate); return; } } } // Load command address (extension features) foreach (RequiredByFeatureAttribute extensionFeature in requiredByExtensions) { string functionName = extensionFeature.EntryPoint ?? defaultName; if (context.Native) { if ((importAddress = context.GetProcAddress(functionName)) != IntPtr.Zero) { BindNativeAPIFunction(function, importAddress); return; } } else { if ((importDelegate = context.GetProcAddressNonNative(functionName)) != null) { function.SetValue(null, importDelegate); return; } } } // Function not implemented: reset function.SetValue(null, null); }
/// <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, GetAddressDelegate 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"); } #if NETSTANDARD1_1 || NETSTANDARD1_4 TypeInfo delegatesClass = typeof(T).GetTypeInfo().GetDeclaredNestedType("Delegates"); Debug.Assert(delegatesClass != null); if (delegatesClass == null) { throw new NotImplementedException("missing Delegates class"); } FieldInfo functionField = delegatesClass.GetDeclaredField("p" + functionName); Debug.Assert(functionField != null); if (functionField == null) { throw new NotImplementedException(String.Format("unable to find function named {0}", functionName)); } #else 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)); } #endif BindAPIFunction(path, getProcAddress, functionContext, functionField, version, extensions); }
/// <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 declaring the /// support of them. /// </summary> /// <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> /// <param name="enableExtensions"></param> protected static void CheckExtensionCommands(KhronosVersion version, ExtensionsCollection extensions, bool enableExtensions) { if (version == null) { throw new ArgumentNullException(nameof(version)); } if (extensions == null) { throw new ArgumentNullException(nameof(extensions)); } var hiddenVersions = new Dictionary <string, List <Type> >(); var hiddenExtensions = new Dictionary <string, bool>(); foreach (FieldInfo fi in _functionContext.Delegates) { var fiDelegateType = (Delegate)fi.GetValue(null); bool commandDefined = fiDelegateType != null; var supportedByFeature = false; // Get the delegate type Type delegateType = fi.DeclaringType?.GetNestedType(fi.Name.Substring(1), BindingFlags.Public | BindingFlags.NonPublic); if (delegateType == null) { continue; // Support fields names not in sync with delegate types } // TODO Why not use 'fi' directly for getting attributes? They should be in sync 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 (commandDefined) { if (hiddenVersionAttrib != null && hiddenExtensionAttrib == null) { if (hiddenVersions.TryGetValue(hiddenVersionAttrib.FeatureName, out List <Type> 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); } } } } // Partial extensions are not supported if (hiddenExtensionAttrib != null && commandDefined == false && hiddenExtensions.ContainsKey(hiddenExtensionAttrib.FeatureName)) { hiddenExtensions[hiddenExtensionAttrib.FeatureName] = false; } } if (!enableExtensions) { return; } var sync = false; foreach (KeyValuePair <string, bool> hiddenExtension in hiddenExtensions.Where(hiddenExtension => hiddenExtension.Value)) { extensions.EnableExtension(hiddenExtension.Key); sync = true; } if (sync) { extensions.SyncMembers(version); } }