Beispiel #1
0
        /// <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);
            }
        }
Beispiel #2
0
        /// <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
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        /// <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);
        }