//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // file="Generator" // Project: StgSharp // AuthorGroup: Nitload // Copyright (c) Nitload. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; namespace GenerateGL.CodeGen { internal sealed class Generator { private static readonly string[] TargetVersions = { "3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6" }; private readonly IReadOnlyDictionary _commands; private readonly IReadOnlyList _features; private readonly string _outputRoot; public Generator( IReadOnlyDictionary commands, IReadOnlyList features, string outputRoot ) { _commands = commands; _features = features; _outputRoot = outputRoot; } public void Run() { if (Directory.Exists(_outputRoot)) { Directory.Delete(_outputRoot, recursive:true); } Directory.CreateDirectory(_outputRoot); List all = new List(); foreach (string v in TargetVersions) { IReadOnlyList cmds = CollectCommandsUpToVersion(v); all.AddRange(cmds); WriteOpenGLFunctionFile(v, cmds); WriteGlContextFile(v, cmds); } WriteMergedGlContextFile(all); } private IReadOnlyList CollectCommandsUpToVersion( string version ) { Version target = Version.Parse(version); IEnumerable selected = _features.Where(f => Version.TryParse(f.Version, out Version? fv) && fv <= target) .SelectMany(f => f.RequiresCommands) .Distinct(StringComparer.Ordinal) .Select(name => _commands.TryGetValue(name, out GlCommand? c) ? c : null) .Where(c => c != null)!; return selected.ToList()!; } private static string DelegateSignature( string ret, IReadOnlyList paramTypes ) { if (paramTypes.Count == 0) { return $"delegate*<{ret}>"; } return $"delegate*<{string.Join(", ", paramTypes)}, {ret}>"; } private static string SafeName( string name ) { return string.IsNullOrWhiteSpace(name) ? "arg" : name switch { "params" => "@params", "event" => "@event", _ => name }; } private static bool ShouldSkip( string name ) { // No skipping; include legacy/fixed-function APIs as well. return false; } private static string TrimGlPrefix( string name ) { if (name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2) { return name.Substring(2); } return name; } private void WriteGlContextFile( string version, IReadOnlyList commands ) { StringBuilder sb = new StringBuilder(); sb.AppendLine("// "); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Version: {version}"); sb.AppendLine("// "); sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("{"); sb.AppendLine(" public unsafe partial struct OpenglContext"); sb.AppendLine(" {"); foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal)) { string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType); List paramTypes = new List(); foreach (GlParam p in cmd.Parameters) { string mapped = CsTypeMapper.MapDelegateParameter(p); paramTypes.Add(mapped); } string decl = DelegateSignature(ret, paramTypes); sb.AppendLine($" internal unsafe {decl} {cmd.Name};"); } sb.AppendLine(" }"); sb.AppendLine("}"); string fileName = Path.Combine(_outputRoot, $"glContext.v{version.Replace('.', '_')}.cs"); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); } private void WriteMergedGlContextFile( IReadOnlyList commands ) { List distinct = commands.Where(c => !ShouldSkip(c.Name)) .GroupBy(c => c.Name, StringComparer.Ordinal) .Select(g => g.First()) .OrderBy(c => c.Name, StringComparer.Ordinal) .ToList(); StringBuilder sb = new StringBuilder(); sb.AppendLine("// "); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine("// Aggregated GL3+ commands"); sb.AppendLine("// "); sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\n"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("{"); sb.AppendLine(" public unsafe partial struct OpenglContext"); sb.AppendLine(" {"); foreach (GlCommand cmd in distinct) { string ret = CsTypeMapper.MapDelegateReturn(cmd.ReturnType); List paramTypes = new List(); foreach (GlParam p in cmd.Parameters) { string mapped = CsTypeMapper.MapDelegateParameter(p); paramTypes.Add(mapped); } string decl = DelegateSignature(ret, paramTypes); sb.AppendLine($" internal unsafe {decl} {cmd.Name};"); } sb.AppendLine(" }"); sb.AppendLine("}"); string fileName = Path.Combine(_outputRoot, "glContext.cs"); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); } private void WriteOpenGLFunctionFile( string version, IReadOnlyList commands ) { StringBuilder sb = new StringBuilder(); sb.AppendLine("// "); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Version: {version}"); sb.AppendLine("// "); sb.AppendLine("using System;\nusing System.Runtime.CompilerServices;\nusing StgSharp.Graphics.OpenGL;\n"); sb.AppendLine("namespace StgSharp.Graphics.OpenGL"); sb.AppendLine("{"); sb.AppendLine(" public unsafe partial class OpenGLFunction"); sb.AppendLine(" {"); foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal)) { string ret = CsTypeMapper.MapReturn(cmd.ReturnType); string rawName = cmd.Name; // original glXxx string methodName = TrimGlPrefix(rawName); List paramList = new List(); List callArgs = new List(); foreach (GlParam p in cmd.Parameters) { string mapped = CsTypeMapper.MapParameter(p); string pname = SafeName(p.Name); paramList.Add($"{mapped} {pname}"); if (mapped == "GlHandle") { callArgs.Add($"{pname}.Value"); } else { callArgs.Add(pname); } } string signature = string.Join(", ", paramList); string call = string.Join(", ", callArgs); sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]"); sb.AppendLine($" public {ret} {methodName}({signature})"); sb.AppendLine(" {"); string prefix = ret == "void" ? string.Empty : "return "; sb.AppendLine($" {prefix}Context.{rawName}({call});"); sb.AppendLine(" }"); sb.AppendLine(); } sb.AppendLine(" }"); sb.AppendLine("}"); string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs"); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); } } }