make some changes to opengl api gen, discussing on rewrite of all opengl binding, making it neat and structral

This commit is contained in:
Youmu Liu
2026-08-25 17:33:45 +08:00
parent 708a915549
commit 65041ff8bc
63 changed files with 14404 additions and 8851 deletions
+170 -60
View File
@@ -32,6 +32,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace GenerateGL.CodeGen
{
@@ -69,6 +70,7 @@ namespace GenerateGL.CodeGen
}
Directory.CreateDirectory(_outputRoot);
List<GlCommand> all = new List<GlCommand>();
List<(string Version, List<GlCommand> Commands)> commandsByVersion = new();
HashSet<string> previous = new HashSet<string>(StringComparer.Ordinal);
foreach (string v in TargetVersions)
{
@@ -79,8 +81,15 @@ namespace GenerateGL.CodeGen
previous.Add(cmd.Name);
}
all.AddRange(versionCmds);
WriteOpenGLFunctionFile(v, versionCmds);
WriteGlContextFile(v, versionCmds);
commandsByVersion.Add((v, versionCmds));
}
IReadOnlyDictionary<string, string> managedNames = ManagedNameResolver.Resolve(
all.Where(ShouldGenerateWrapper));
foreach ((string version, List<GlCommand> commands) in commandsByVersion)
{
WriteOpenGLFunctionFile(version, commands, managedNames);
UniformOverloadEmitter.Write(_outputRoot, version, commands);
}
WriteMergedGlContextFile(all);
WriteCommandHistoryFile(BuildCommandHistory());
@@ -249,41 +258,7 @@ namespace GenerateGL.CodeGen
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glCommandHistory.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteGlContextFile(
string version,
IReadOnlyList<GlCommand> commands
)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Version: {version}");
sb.AppendLine("// </auto-generated>");
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, cmd.ReturnPointerLevel);
List<string> paramTypes = new List<string>();
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");
string fileName = Path.Combine(_outputRoot, "GlCommandHistory.g.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
@@ -322,13 +297,14 @@ namespace GenerateGL.CodeGen
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, "glContext.cs");
string fileName = Path.Combine(_outputRoot, "OpenglContext.g.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private void WriteOpenGLFunctionFile(
string version,
IReadOnlyList<GlCommand> commands
IReadOnlyList<GlCommand> commands,
IReadOnlyDictionary<string, string> managedNames
)
{
StringBuilder sb = new StringBuilder();
@@ -341,33 +317,52 @@ namespace GenerateGL.CodeGen
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial class OpenGLFunction");
sb.AppendLine(" {");
foreach (GlCommand cmd in commands.OrderBy(c => c!.Name, StringComparer.Ordinal))
foreach (GlCommand cmd in commands.Where(ShouldGenerateWrapper)
.OrderBy(c => c!.Name, StringComparer.Ordinal))
{
string ret = CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
bool returnsHandle = CsTypeMapper.IsHandleReturn(cmd);
string ret = returnsHandle
? "GlHandle"
: CsTypeMapper.MapReturn(cmd.ReturnType, cmd.ReturnPointerLevel);
string rawName = cmd.Name; // original glXxx
string methodName = TrimGlPrefix(rawName);
string methodName = managedNames[rawName];
ManagedCommandPlan commandPlan = ManagedCommandPlan.Create(cmd);
List<string> paramList = new List<string>();
List<string> callArgs = new List<string>();
List<(string Type,string Name)> pinned = new();
List<(string Type,string Name,bool IsOut)> pinned = new();
foreach (ManagedParameterPlan parameterPlan in commandPlan.PublicParameters)
{
GlParam p = parameterPlan.Source;
string pname = SafeName(p.Name);
paramList.Add($"{parameterPlan.PublicType} {pname}");
if (parameterPlan.IsSpan)
{
pinned.Add((parameterPlan.ElementType!, pname, false));
} else if (parameterPlan.Kind == ManagedParameterKind.Out) {
pinned.Add((parameterPlan.ElementType!, pname, true));
}
}
foreach (GlParam p in cmd.Parameters)
{
string pname = SafeName(p.Name);
if (CsTypeMapper.TryMapConstPointerToSpan(p, out string spanElementType))
if (commandPlan.DerivedLengthSources.TryGetValue(
p.Name, out string? sourceSpanName))
{
string nativeType = CsTypeMapper.MapParameter(p);
callArgs.Add($"checked(({nativeType}){SafeName(sourceSpanName)}.Length)");
continue;
}
ManagedParameterPlan parameterPlan = commandPlan.GetParameter(p.Name);
if (parameterPlan.IsSpan || parameterPlan.Kind == ManagedParameterKind.Out)
{
paramList.Add($"ReadOnlySpan<{spanElementType}> {pname}");
pinned.Add((spanElementType, pname));
callArgs.Add($"{pname}Ptr");
} else if (parameterPlan.PublicType == "GlHandle")
{
callArgs.Add($"{pname}.Value");
} else
{
string mapped = CsTypeMapper.MapParameter(p);
paramList.Add($"{mapped} {pname}");
if (mapped == "GlHandle")
{
callArgs.Add($"{pname}.Value");
} else
{
callArgs.Add(pname);
}
callArgs.Add(pname);
}
}
string signature = string.Join(", ", paramList);
@@ -375,20 +370,29 @@ namespace GenerateGL.CodeGen
sb.AppendLine($" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public {ret} {methodName}({signature})");
sb.AppendLine(" {");
foreach ((string _, string name, bool isOut) in pinned.Where(p => p.IsOut)) {
sb.AppendLine($" {name} = default;");
}
AppendSpanLengthGuards(sb, cmd, commandPlan);
string prefix = ret == "void" ? string.Empty : "return ";
string callExpression = $"Context.{rawName}({call})";
if (returnsHandle) {
callExpression = $"GlHandle.Create({callExpression})";
}
if (pinned.Count == 0)
{
sb.AppendLine($" {prefix}Context.{rawName}({call});");
sb.AppendLine($" {prefix}{callExpression};");
} else
{
string indent = " ";
foreach ((string Type, string Name) pin in pinned)
foreach ((string Type, string Name, bool IsOut) pin in pinned)
{
sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pin.Name})");
string pinSource = pin.IsOut ? $"&{pin.Name}" : pin.Name;
sb.AppendLine($"{indent}fixed ({pin.Type}* {pin.Name}Ptr = {pinSource})");
sb.AppendLine($"{indent}{{");
indent += " ";
}
sb.AppendLine($"{indent}{prefix}Context.{rawName}({call});");
sb.AppendLine($"{indent}{prefix}{callExpression};");
for (int i = 0; i < pinned.Count; i++)
{
indent = indent.Substring(0, indent.Length - 4);
@@ -401,10 +405,116 @@ namespace GenerateGL.CodeGen
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(_outputRoot, $"glFunction.v{version.Replace('.', '_')}.cs");
string fileName = Path.Combine(
_outputRoot,
$"OpenGLFunction.Raw.v{version.Replace('.', '_')}.g.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private static void AppendSpanLengthGuards(
StringBuilder sb,
GlCommand command,
ManagedCommandPlan plan
)
{
foreach ((string lengthName, string sourceName) in plan.DerivedLengthSources)
{
foreach (ManagedParameterPlan related in command.Parameters
.Select(p => plan.GetParameter(p.Name))
.Where(p => p.IsSpan && p.Source.Length == lengthName &&
p.Source.Name != sourceName))
{
string relatedName = SafeName(related.Source.Name);
string safeSourceName = SafeName(sourceName);
sb.AppendLine($" if ({relatedName}.Length != {safeSourceName}.Length) {{");
sb.AppendLine($" throw new ArgumentException(\"Span lengths controlled by {lengthName} must match.\", nameof({relatedName}));");
sb.AppendLine(" }");
}
}
foreach (ManagedParameterPlan parameter in command.Parameters
.Select(p => plan.GetParameter(p.Name)).Where(p => p.IsSpan))
{
string? expectedLength = BuildExpectedLengthExpression(parameter.Source, plan);
if (expectedLength is null) {
continue;
}
string parameterName = SafeName(parameter.Source.Name);
sb.AppendLine($" if ({parameterName}.Length < {expectedLength}) {{");
sb.AppendLine($" throw new ArgumentException(\"Span is shorter than the OpenGL length expression '{parameter.Source.Length}'.\", nameof({parameterName}));");
sb.AppendLine(" }");
}
}
private static string? BuildExpectedLengthExpression(
GlParam parameter,
ManagedCommandPlan plan
)
{
string length = parameter.Length?.Replace(" ", string.Empty,
StringComparison.Ordinal) ?? string.Empty;
if (length.Length == 0 || length.StartsWith("COMPSIZE(", StringComparison.Ordinal) ||
plan.DerivedLengthSources.ContainsKey(length)) {
return null;
}
if (int.TryParse(length, NumberStyles.None, CultureInfo.InvariantCulture,
out int constantLength)) {
return constantLength.ToString(CultureInfo.InvariantCulture);
}
ManagedParameterPlan? scalar = plan.PublicParameters.FirstOrDefault(p =>
p.Source.PointerLevel == 0 && p.Source.Name == length);
if (scalar is not null) {
return $"checked((int){SafeName(length)})";
}
Match multiply = Regex.Match(
length, "^(?<name>[A-Za-z_][A-Za-z0-9_]*)\\*(?<factor>[0-9]+)$",
RegexOptions.CultureInvariant);
if (!multiply.Success) {
multiply = Regex.Match(
length, "^(?<factor>[0-9]+)\\*(?<name>[A-Za-z_][A-Za-z0-9_]*)$",
RegexOptions.CultureInvariant);
}
if (multiply.Success) {
return $"checked((int){SafeName(multiply.Groups["name"].Value)} * {multiply.Groups["factor"].Value})";
}
Match divide = Regex.Match(
length, "^(?<name>[A-Za-z_][A-Za-z0-9_]*)/(?<divisor>[0-9]+)$",
RegexOptions.CultureInvariant);
return divide.Success
? $"checked((int){SafeName(divide.Groups["name"].Value)} / {divide.Groups["divisor"].Value})"
: null;
}
private bool ShouldGenerateWrapper(
GlCommand command
)
{
if (UniformOverloadEmitter.IsSetterCommand(command)) {
return false;
}
return !IsRedundantVertexAttribVectorCommand(command);
}
private bool IsRedundantVertexAttribVectorCommand(
GlCommand command
)
{
string name = command.Name;
if (!name.StartsWith("glVertexAttrib", StringComparison.Ordinal) ||
!name.EndsWith('v')) {
return false;
}
string scalarCommandName = name.Substring(0, name.Length - 1);
return _commands.ContainsKey(scalarCommandName);
}
private enum CommandHistoryAction
{