/// <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> /// 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> /// 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 (extensions == null) { throw new ArgumentNullException(nameof(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> /// Determine whether an API implementation removes this feature. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the API version. /// </param> /// <param name="extensions"> /// The <see cref="KhronosApi.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 removed 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 IsRemoved(KhronosVersion version, KhronosApi.ExtensionsCollection extensions) { if (version == null) { throw new ArgumentNullException(nameof(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)) { return(false); } // API version must be greater than or equal to the required version return(version >= FeatureVersion); } if (extensions != null) { // Check compatible API if (!Regex.IsMatch(version.Api, Api)) { return(false); } // Last chance: extension name return(extensions.HasExtensions(FeatureName)); } 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. /// </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("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)); }
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> /// 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="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" /> is less than 0. /// </exception> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="api" /> is null. /// </exception> public CoreExtensionAttribute(int major, int minor, string api) { Version = new KhronosVersion(major, minor, 0, api); }
public static void BindAPIFunction(string functionName, KhronosVersion version, ExtensionsCollection extensions, GraphicsContext context) { BindAPIFunction(_functionContext.GetFunction(functionName), version, extensions, context); }
/// <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); }
/// <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) { BindAPI <T>(path, getAddress, version, 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) { FunctionContext functionContext = GetFunctionContext(typeof(T)); Debug.Assert(functionContext != null); BindAPIFunction(path, getProcAddress, functionContext.GetFunction(functionName), version, extensions); }
protected internal void SyncMembers(KhronosVersion version) { if (version == null) { throw new ArgumentNullException("version"); } Type thisType = GetType(); #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <FieldInfo> thisTypeFields = thisType.GetTypeInfo().DeclaredFields; #else IEnumerable <FieldInfo> thisTypeFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public); #endif foreach (FieldInfo fieldInfo in thisTypeFields) { // Check boolean field (defensive) Debug.Assert(fieldInfo.FieldType == typeof(bool)); if (fieldInfo.FieldType != typeof(bool)) { continue; } bool support = false; // Support by extension #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <Attribute> coreAttributes = fieldInfo.GetCustomAttributes(typeof(CoreExtensionAttribute)); #else IEnumerable <Attribute> coreAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(CoreExtensionAttribute)); #endif if (coreAttributes != null) { foreach (CoreExtensionAttribute coreAttribute in coreAttributes) { if (version.Api == coreAttribute.Version.Api && version >= coreAttribute.Version) { support |= true; break; } } } // Support by extension #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <Attribute> extensionAttributes = fieldInfo.GetCustomAttributes(typeof(ExtensionAttribute)); #else IEnumerable <Attribute> extensionAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(ExtensionAttribute)); #endif if (extensionAttributes != null) { foreach (ExtensionAttribute extensionAttribute in extensionAttributes) { if (_ExtensionsRegistry.ContainsKey(extensionAttribute.ExtensionName)) { support |= true; break; } } } fieldInfo.SetValue(this, support); } }
/// <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 NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE Attribute[] attrRequired = new List <Attribute>(function.GetCustomAttributes(typeof(RequiredByFeatureAttribute))).ToArray(); // XXX #else Attribute[] attrRequired = Attribute.GetCustomAttributes(function, typeof(RequiredByFeatureAttribute)); #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 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) { 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. /// </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> /// 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); } }
/// <summary> /// Set all fields of this ExtensionsCollection, depending on current extensions. /// </summary> /// <param name="version"> /// The <see cref="KhronosVersion"/> that specifies the context version/API. It can be null. /// </param> protected internal void SyncMembers(KhronosVersion version) { Type thisType = GetType(); #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <FieldInfo> thisTypeFields = thisType.GetTypeInfo().DeclaredFields; #else IEnumerable <FieldInfo> thisTypeFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public); #endif foreach (FieldInfo fieldInfo in thisTypeFields) { // Check boolean field (defensive) // Debug.Assert(fieldInfo.FieldType == typeof(bool)); if (fieldInfo.FieldType != typeof(bool)) { continue; } bool support = false; // Support by extension #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <Attribute> extensionAttributes = fieldInfo.GetCustomAttributes(typeof(ExtensionAttribute)); #else IEnumerable <Attribute> extensionAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(ExtensionAttribute)); #endif // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (ExtensionAttribute extensionAttribute in extensionAttributes) { if (!_ExtensionsRegistry.ContainsKey(extensionAttribute.ExtensionName)) { continue; } if (version != null && version.Api != null && extensionAttribute.Api != null && !Regex.IsMatch(version.Api, "^" + extensionAttribute.Api + "$")) { continue; } support = true; break; } // Support by version if (version != null && !support) { #if NETSTANDARD1_1 || NETSTANDARD1_4 || NETCORE IEnumerable <Attribute> coreAttributes = fieldInfo.GetCustomAttributes(typeof(CoreExtensionAttribute)); #else IEnumerable <Attribute> coreAttributes = Attribute.GetCustomAttributes(fieldInfo, typeof(CoreExtensionAttribute)); #endif // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (CoreExtensionAttribute coreAttribute in coreAttributes) { if (version.Api != coreAttribute.Version.Api || version < coreAttribute.Version) { continue; } support = true; break; } } fieldInfo.SetValue(this, support); } }
/// <summary> /// Link delegates fields using import declarations. /// </summary> /// <param name="version"></param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="path"/> or <paramref name="getAddress"/> is null. /// </exception> internal static void BindAPI <T>(KhronosVersion version) { BindAPI <T>(version, 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); }