Ejemplo n.º 1
0
        public static GLSpec FromFile(string xmlFile)
        {
            XDocument doc  = XDocument.Load(xmlFile);
            GLSpec    spec = new GLSpec();

            spec.HeaderComment = doc.Root.Element("comment").Value;

            foreach (var e in doc.Root.Elements("feature"))
            {
                // OpenGL is super special because it simply includes all enums and commands
                if (e.Attribute("api").Value == "gl" && e.Attribute("number").Value == "1.0")
                {
                    AddVersion1(spec, e);
                }
                // OpenGL 3.0 and up have Compatibility profile and Core profile
                else if (e.Attribute("api").Value == "gl" && int.Parse(e.Attribute("number").Value[0].ToString()) >= 3)
                {
                    AddVersion(spec, e, "Compatibility");
                    AddVersion(spec, e, "Core");
                }
                // OpenGL pre-3.0 and OpenGL ES don't have profiles
                else
                {
                    AddVersion(spec, e, string.Empty);
                }
            }

            return(spec);
        }
Ejemplo n.º 2
0
 private static void WriteFileHeader(CodeWriter writer, GLSpec spec)
 {
     writer.WriteLine("// This file was autogenerated by GLCSGen on {0} UTC", DateTime.UtcNow);
     writer.WriteLine("// Original copyright from gl.xml:");
     foreach (var l in spec.HeaderComment.Split('\n', '\r'))
     {
         writer.WriteLine("// {0}", l);
     }
     writer.WriteLine();
     writer.WriteLine("using System;");
     writer.WriteLine("using System.Diagnostics;");
     writer.WriteLine("using System.Reflection;");
     writer.WriteLine("using System.Runtime.InteropServices;");
     writer.WriteLine();
     writer.WriteLine("namespace OpenGL");
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Super special OpenGL 1 version handler
        /// </summary>
        private static void AddVersion1(GLSpec spec, XElement versionElem)
        {
            var version = new GLVersion
            {
                Api     = "GL",
                Number  = "1.0",
                Profile = string.Empty,
                Name    = "GL10"
            };

            // First we process commands like any other version
            foreach (var require in versionElem.Elements("require"))
            {
                foreach (var commandElem in require.Elements("command"))
                {
                    var command = new GLCommand {
                        Name = commandElem.Attribute("name").Value
                    };
                    command.Initialize(commandElem.Document);
                    version.Commands.Add(command);
                }
            }

            // Then we go through all included commands and use all the groups on any parameters
            // to include all the enums we need from the groups section
            foreach (var group in (from c in version.Commands from p in c.Parameters select p.Group).Distinct())
            {
                if (string.IsNullOrEmpty(group))
                {
                    continue;
                }

                foreach (var enumElem in versionElem.Document.Root.Element("groups").Elements("group").Where(e => e.Attribute("name").Value == group).Elements("enum"))
                {
                    if (!version.Enums.ContainsKey(enumElem.Attribute("name").Value))
                    {
                        string enumName = enumElem.Attribute("name").Value;
                        version.Enums.Add(enumName, GetEnumValue(versionElem, enumName));
                    }
                }
            }

            spec.Versions.Add(version);
        }
Ejemplo n.º 4
0
        private static void CreateGLInterop(GLSpec spec, DirectoryInfo outDir, string api)
        {
            // Get the unique list of all commands
            List <GLCommand> commands = new List <GLCommand>();

            foreach (var version in spec.Versions)
            {
                if (version.Api != api)
                {
                    continue;
                }

                foreach (var c in version.Commands)
                {
                    if (commands.Find(c2 => c2.Name == c.Name) == null)
                    {
                        commands.Add(c);
                    }
                }
            }

            // Just to be pretty, sort them by name :)
            commands.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));

            // Create the interop file containing all of the delegate types and instances
            using (var writer = new CodeWriter(Path.Combine(outDir.FullName, api + "Interop.cs")))
            {
                WriteFileHeader(writer, spec);
                writer.WriteOpenBrace();
                writer.WriteLine("internal static class {0}Interop", api);
                writer.WriteOpenBrace();
                WriteDelegates(writer, "public", commands);
                writer.WriteCloseBrace();
                writer.WriteCloseBrace();
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Creates a given version.
        /// </summary>
        private static void AddVersion(GLSpec spec, XElement versionElem, string profile)
        {
            var version = new GLVersion
            {
                Api     = versionElem.Attribute("api").Value.ToUpper(),
                Number  = versionElem.Attribute("number").Value,
                Profile = profile
            };

            if (version.Api != "GL")
            {
                version.Api = "GLES";
            }

            version.Name = version.Api + version.Number.Replace(".", "");
            if (!string.IsNullOrEmpty(profile))
            {
                version.Name += "" + profile;
            }

            // Add all enums and commands from previous version
            for (int i = spec.Versions.Count - 1; i >= 0; i--)
            {
                var previousVersion = spec.Versions[i];
                if (previousVersion.Api == version.Api &&
                    (previousVersion.Profile == string.Empty || previousVersion.Profile == version.Profile))
                {
                    foreach (var pair in previousVersion.Enums)
                    {
                        version.Enums.Add(pair.Key, pair.Value);
                    }

                    foreach (var command in previousVersion.Commands)
                    {
                        version.Commands.Add(command.Clone());
                    }

                    // only handle previous one version of same API and profile since it will have folded
                    // all enums/commands in turn
                    break;
                }
            }

            // Include all new enums and commands
            foreach (var require in versionElem.Elements("require"))
            {
                foreach (var enumElem in require.Elements("enum"))
                {
                    if (!version.Enums.ContainsKey(enumElem.Attribute("name").Value))
                    {
                        string enumName = enumElem.Attribute("name").Value;
                        version.Enums.Add(enumName, GetEnumValue(versionElem, enumName));
                    }
                }

                foreach (var commandElem in require.Elements("command"))
                {
                    var command = new GLCommand {
                        Name = commandElem.Attribute("name").Value
                    };
                    if (version.Commands.Find(c => c.Name == command.Name) != null)
                    {
                        continue;
                    }

                    command.Initialize(commandElem.Document);
                    version.Commands.Add(command);
                }
            }

            // Remove any enums and commands
            foreach (var remove in versionElem.Elements("remove"))
            {
                if (remove.Attribute("profile") == null ||
                    remove.Attribute("profile").Value.ToLower() == profile.ToLower())
                {
                    foreach (var en in remove.Elements("enum"))
                    {
                        version.Enums.Remove(en.Attribute("name").Value);
                    }
                    foreach (var en in remove.Elements("command"))
                    {
                        version.Commands.RemoveAll(c => c.Name == en.Attribute("name").Value);
                    }
                }
            }

            spec.Versions.Add(version);
        }
Ejemplo n.º 6
0
        static void Main(string[] args)
        {
            // Isolated mode generates files like we always have, with each C# file being completely standalone
            // with its own set of delegates under the hood. Without isolated mode, the new default is to generate
            // a single interop type per assembly and have each GL version share those function pointers. This
            // reduces assembly size by removing lots and lots of duplicate delegate types and function pointers.
            bool isolated = args.Contains("--isolated");

            // Since error handling code adds size to the generated assemblies, there is now an option to remove
            // it for people who want extremely trim assemblies and don't mind dealing with harder-to-read error
            // messages when function pointers fail to load.
            bool errorHandling = !args.Contains("--no-error-check");

            // Walk up to the solution file so we can then go into GL-CS and write to the C# files directly
            DirectoryInfo directory = new DirectoryInfo(Directory.GetCurrentDirectory());

            while (directory != null && !File.Exists(Path.Combine(directory.FullName, "GL-CS.sln")))
            {
                directory = directory.Parent;
            }

            // If the solution wasn't found (maybe we're not running from the bin directory)
            // we'll just write out to the current directory
            if (directory == null)
            {
                directory = new DirectoryInfo(Directory.GetCurrentDirectory());
            }

            // Create our two directories for output files
            var glDirectory   = new DirectoryInfo(Path.Combine(directory.FullName, "GL-CS"));
            var glesDirectory = new DirectoryInfo(Path.Combine(directory.FullName, "GLES-CS"));

            // Load the spec
            var spec = GLSpec.FromFile(Path.Combine(directory.FullName, "GLCSGen", "gl.xml"));

            //using (var stream = File.Create("spec.xml"))
            //{
            //    var serializer = new XmlSerializer(typeof(GLSpec));
            //    serializer.Serialize(stream, spec);
            //}

            // In non-isolated mode we have to build up a list of all the commands in all versions of
            // the module, and write them into a single GLInterop type.
            if (!isolated)
            {
                CreateGLInterop(spec, glDirectory, "GL");
                CreateGLInterop(spec, glesDirectory, "GLES");
            }

            foreach (var version in spec.Versions)
            {
                var dir = version.Api == "GL" ? glDirectory : glesDirectory;

                using (var writer = new CodeWriter(Path.Combine(dir.FullName, version.Name + ".cs")))
                {
                    WriteFileHeader(writer, spec);
                    writer.WriteOpenBrace();
                    writer.WriteLine("public static class {0}", version.Name);
                    writer.WriteOpenBrace();

                    writer.WriteLine("#region Enums");
                    foreach (var e in version.Enums)
                    {
                        string type = IsUint(e.Value) ? "uint" : "ulong";
                        writer.WriteLine("public static {0} {1} = {2};", type, e.Key, e.Value);
                    }
                    writer.WriteLine("#endregion");

                    writer.WriteLine();

                    writer.WriteLine("#region Commands");
                    foreach (var c in version.Commands)
                    {
                        StringBuilder builder = new StringBuilder("public static ");
                        builder.Append(ConvertGLType(c.ReturnType));
                        builder.AppendFormat(" {0}(", c.Name);
                        BuildParameterList(c, builder);
                        builder.Append(")");
                        writer.WriteLine(builder.ToString());

                        writer.WriteOpenBrace();

                        builder.Clear();
                        if (c.ReturnType != "void")
                        {
                            builder.Append("return ");
                        }
                        if (!isolated)
                        {
                            builder.Append(version.Api);
                            builder.Append("Interop.");
                        }
                        builder.AppendFormat("{0}Ptr(", c.Name);

                        if (c.Parameters.Count > 0)
                        {
                            foreach (var p in c.Parameters)
                            {
                                var name = p.Name;

                                // Add @ to start of any names that are C# keywords to avoid conflict
                                if (name == "params" || name == "string" || name == "ref" || name == "base")
                                {
                                    name = "@" + name;
                                }

                                builder.AppendFormat("{0}, ", name);
                            }
                            builder.Length -= 2;
                        }

                        builder.Append(");");
                        writer.WriteLine(builder.ToString());

                        writer.WriteCloseBrace();
                    }
                    writer.WriteLine("#endregion");

                    if (isolated)
                    {
                        writer.WriteLine();

                        writer.WriteLine("#region Command Delegates");
                        WriteDelegates(writer, "private", version.Commands);
                        writer.WriteLine("#endregion");
                    }

                    writer.WriteLine();
                    writer.WriteLine("#region Interop");
                    writer.WriteLine("public static Func<string, IntPtr> GetProcAddress = null;");
                    writer.WriteLine();
                    writer.WriteLine("public static void LoadAllFunctions()");
                    writer.WriteOpenBrace();
                    foreach (var c in version.Commands)
                    {
                        var    delegateName   = isolated ? c.Name : (version.Api + "Interop." + c.Name);
                        string getFuncPtrCode = string.Format("{0}Ptr = ({0}Func)Marshal.GetDelegateForFunctionPointer(GetProcAddress(\"{1}\"), typeof({0}Func));", delegateName, c.Name);

                        if (errorHandling)
                        {
                            writer.WriteLine("try {{ {0} }}", getFuncPtrCode);
                            writer.WriteLine("catch {{ throw new InvalidOperationException(\"Failed to get function pointer for '{0}'.\"); }}", c.Name);
                        }
                        else
                        {
                            writer.WriteLine(getFuncPtrCode);
                        }
                    }
                    writer.WriteCloseBrace();
                    writer.WriteLine();
                    writer.WriteLine("public static void LoadFunction(string name)");
                    writer.WriteOpenBrace();
                    if (errorHandling)
                    {
                        writer.WriteLine("try");
                        writer.WriteOpenBrace();
                    }
                    writer.WriteLine("var memberInfo = typeof({0}).GetField(name + \"Ptr\", BindingFlags.{1} | BindingFlags.Static);", isolated ? version.Name : (version.Api + "Interop"), isolated ? "NonPublic" : "Public");
                    if (errorHandling)
                    {
                        writer.WriteLine("Debug.Assert(memberInfo != null, string.Format(\"Failed to find function delegate. Ensure '{0}' is a valid OpenGL function.\", name));");
                    }
                    writer.WriteLine("var procAddr = GetProcAddress(name);");
                    if (errorHandling)
                    {
                        writer.WriteLine("Debug.Assert(procAddr != IntPtr.Zero, string.Format(\"Failed to find function address. Ensure '{0}' is a valid OpenGL function.\", name));");
                    }
                    writer.WriteLine("var funcPtr = Marshal.GetDelegateForFunctionPointer(procAddr, memberInfo.FieldType);");
                    if (errorHandling)
                    {
                        writer.WriteLine("Debug.Assert(funcPtr != null, string.Format(\"Failed to convert function address to delegate for '{0}'.\", name));");
                    }
                    writer.WriteLine("memberInfo.SetValue(null, funcPtr);");
                    if (errorHandling)
                    {
                        writer.WriteCloseBrace();
                        writer.WriteLine("catch");
                        writer.WriteOpenBrace();
                        writer.WriteLine("throw new InvalidOperationException(string.Format(\"Failed to load function '{0}'.\", name));");
                        writer.WriteCloseBrace();
                    }
                    writer.WriteCloseBrace();
                    writer.WriteLine("#endregion");

                    writer.WriteCloseBrace();
                    writer.WriteCloseBrace();
                }
            }
        }