make new opengl source generator

This commit is contained in:
Youmu Liu
2026-08-26 20:19:25 +08:00
parent 75bf7fc139
commit 5db9f39a46
7 changed files with 1151 additions and 52 deletions
@@ -49,22 +49,41 @@ namespace StgSharp.GenerateGL.Generation
{
ArgumentNullException.ThrowIfNull(registry);
IReadOnlyList<GlCommandDefinition> commands = ReadCoreCommands(registry);
IReadOnlyList<GlCommandGroup> commandGroups = ReadCoreCommandGroups(registry);
IReadOnlyList<GlCommandDefinition> commands = Array.AsReadOnly(
commandGroups.SelectMany(value => value.Commands).ToArray());
GlEquivalentTypeMapper typeMapper = new GlEquivalentTypeMapper(registry);
return new GeneratedFileSet(
new[]
{
new GeneratedFile("glconst.cs", GenerateConstants(registry)),
ManagedApiPlanner apiPlanner = new ManagedApiPlanner(typeMapper);
List<ManagedCommandGroup> managedGroups = commandGroups.Select(
group => new ManagedCommandGroup(
group,
apiPlanner.Plan(group.Commands))).ToList();
ManagedApiPlanner.Validate(
managedGroups.SelectMany(value => value.Methods));
List<GeneratedFile> files = new List<GeneratedFile>
{
new GeneratedFile("glconst.cs", GenerateConstants(registry)),
new GeneratedFile(
"OpenglContext.g.cs",
GenerateContext(commands, typeMapper)),
};
for (int index = 0; index < managedGroups.Count; index++)
{
ManagedCommandGroup managedGroup = managedGroups[index];
GlCommandGroup group = managedGroup.Source;
files.Add(
new GeneratedFile(
"OpenglContext.g.cs",
GenerateContext(commands, typeMapper)),
new GeneratedFile(
"OpenGLFunction.Raw.g.cs",
GenerateFunctions(commands, typeMapper)),
});
group.FileName,
GenerateFunctions(
managedGroup.Methods,
group.Description,
index == 0)));
}
return new GeneratedFileSet(files);
}
private static ReadOnlyCollection<GlCommandDefinition> ReadCoreCommands(
private static ReadOnlyCollection<GlCommandGroup> ReadCoreCommandGroups(
GlRegistryModel registry
)
{
@@ -82,6 +101,8 @@ namespace StgSharp.GenerateGL.Generation
List<string> order = new List<string>();
HashSet<string> known = new HashSet<string>(StringComparer.Ordinal);
HashSet<string> active = new HashSet<string>(StringComparer.Ordinal);
Dictionary<string, string> introducedVersions = new Dictionary<string, string>(
StringComparer.Ordinal);
foreach (GlFeatureDefinition feature in registry.Features.Where(
value => string.Equals(value.Api, ApiName, StringComparison.Ordinal)))
{
@@ -103,6 +124,7 @@ namespace StgSharp.GenerateGL.Generation
if (known.Add(reference.Name))
{
order.Add(reference.Name);
introducedVersions.Add(reference.Name, feature.Number);
}
}
else
@@ -113,8 +135,72 @@ namespace StgSharp.GenerateGL.Generation
}
}
return Array.AsReadOnly(
order.Where(active.Contains).Select(value => definitions[value]).ToArray());
List<GlCommandDefinition> foundation = new List<GlCommandDefinition>();
Dictionary<string, List<GlCommandDefinition>> versionGroups = new Dictionary<string, List<GlCommandDefinition>>(
StringComparer.Ordinal);
List<string> versionOrder = new List<string>();
foreach (string commandName in order.Where(active.Contains))
{
string version = introducedVersions[commandName];
if (IsFoundationVersion(version))
{
foundation.Add(definitions[commandName]);
continue;
}
if (!versionGroups.TryGetValue(
version,
out List<GlCommandDefinition>? commands))
{
commands = new List<GlCommandDefinition>();
versionGroups.Add(version, commands);
versionOrder.Add(version);
}
commands.Add(definitions[commandName]);
}
List<GlCommandGroup> groups = new List<GlCommandGroup>
{
new GlCommandGroup(
"OpenGLFunction.Base.g.cs",
"Desktop OpenGL forwarding methods introduced through 3.2 and retained by the target core profile",
Array.AsReadOnly(foundation.ToArray())),
};
foreach (string version in versionOrder)
{
groups.Add(
new GlCommandGroup(
$"OpenGLFunction.v{version.Replace('.', '_')}.g.cs",
$"Desktop OpenGL {version} core-profile forwarding methods",
Array.AsReadOnly(versionGroups[version].ToArray())));
}
return Array.AsReadOnly(groups.ToArray());
}
private static bool IsFoundationVersion(
string version
)
{
string[] components = version.Split('.');
if (components.Length != 2 ||
!int.TryParse(
components[0],
NumberStyles.None,
CultureInfo.InvariantCulture,
out int major) ||
!int.TryParse(
components[1],
NumberStyles.None,
CultureInfo.InvariantCulture,
out int minor))
{
throw new InvalidDataException(
$"OpenGL feature version '{version}' is not a major.minor number.");
}
return major < 3 || major == 3 && minor <= 2;
}
private static bool AppliesToCoreGl(
@@ -319,62 +405,53 @@ namespace StgSharp.GenerateGL.Generation
}
private static string GenerateFunctions(
IReadOnlyList<GlCommandDefinition> commands,
GlEquivalentTypeMapper typeMapper
IReadOnlyList<ManagedMethodDefinition> methods,
string description,
bool includeContextState
)
{
StringBuilder builder = new StringBuilder(512 * 1024);
AppendGeneratedHeader(builder, "Desktop OpenGL core forwarding methods");
AppendGeneratedHeader(builder, description);
builder.Append("using StgSharp.Mathematics.Graphics;\n\n");
builder.Append("using System;\n");
builder.Append("using System.Runtime.CompilerServices;\n\n");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
builder.Append("{\n");
builder.Append(" public unsafe partial class OpenGLFunction\n");
builder.Append(" {\n");
builder.Append("\n private readonly OpenglContext* __context;\n");
builder.Append("\n internal OpenGLFunction(OpenglContext* context)\n");
builder.Append(" {\n");
builder.Append(" __context = context;\n");
builder.Append(" }\n");
foreach (GlCommandDefinition command in commands)
if (includeContextState)
{
builder.Append("\n private readonly OpenglContext* __context;\n");
builder.Append("\n internal OpenGLFunction(OpenglContext* context)\n");
builder.Append(" {\n");
builder.Append(" __context = context;\n");
builder.Append(" }\n");
}
foreach (ManagedMethodDefinition method in methods)
{
string returnType = typeMapper.ReadReturnType(command);
string methodName = ReadMethodName(command.Name);
builder.Append("\n [MethodImpl(MethodImplOptions.AggressiveInlining)]\n");
builder.Append(" public ");
builder.Append(returnType);
builder.Append(method.ReturnType);
builder.Append(' ');
builder.Append(methodName);
builder.Append(method.Name);
builder.Append('(');
for (int index = 0; index < command.Parameters.Count; index++)
for (int index = 0; index < method.Parameters.Count; index++)
{
if (index != 0)
{
builder.Append(", ");
}
GlParameterDefinition parameter = command.Parameters[index];
builder.Append(typeMapper.ReadParameterType(parameter));
ManagedParameterDefinition parameter = method.Parameters[index];
builder.Append(parameter.Type);
builder.Append(' ');
builder.Append(EscapeIdentifier(parameter.Name));
}
builder.Append(")\n");
builder.Append(" {\n");
builder.Append(" ");
if (returnType != "void")
{
builder.Append("return ");
}
builder.Append("__context->");
builder.Append(command.Name);
builder.Append('(');
builder.Append(
string.Join(
", ",
command.Parameters.Select(
value => EscapeIdentifier(value.Name))));
builder.Append(");\n");
AppendInvocation(builder, method);
builder.Append(" }\n");
}
@@ -383,14 +460,252 @@ namespace StgSharp.GenerateGL.Generation
return builder.ToString();
}
private static string ReadMethodName(
string commandName
private static void AppendInvocation(
StringBuilder builder,
ManagedMethodDefinition method
)
{
return commandName.StartsWith("gl", StringComparison.Ordinal) &&
commandName.Length > 2
? commandName[2..]
: commandName;
switch (method.Invocation)
{
case DirectInvocation direct:
AppendDirectInvocation(builder, method, direct);
break;
case PinnedSpanInvocation pinned:
AppendPinnedSpanInvocation(builder, method, pinned);
break;
case PinnedVectorSpanInvocation pinnedVector:
AppendPinnedVectorSpanInvocation(builder, method, pinnedVector);
break;
case Matrix4Invocation matrix:
AppendMatrix4Invocation(builder, method, matrix);
break;
default:
throw new InvalidOperationException(
$"Unsupported managed invocation '{method.Invocation.GetType().Name}'.");
}
}
private static void AppendDirectInvocation(
StringBuilder builder,
ManagedMethodDefinition method,
DirectInvocation invocation
)
{
List<int> fixedParameters = new List<int>();
for (int index = 0; index < method.Parameters.Count; index++)
{
if (method.Parameters[index].IsHandleSpan)
{
fixedParameters.Add(index);
}
}
int indentation = 12;
foreach (int parameterIndex in fixedParameters)
{
ManagedParameterDefinition parameter = method.Parameters[parameterIndex];
builder.Append(' ', indentation);
builder.Append("fixed (GlHandle* __");
builder.Append(parameter.Name);
builder.Append(" = ");
builder.Append(EscapeIdentifier(parameter.Name));
builder.Append(")\n");
builder.Append(' ', indentation);
builder.Append("{\n");
indentation += 4;
}
builder.Append(' ', indentation);
if (method.WrapHandleReturn)
{
builder.Append("return GlHandle.Create(");
}
else if (method.ReturnType != "void")
{
builder.Append("return ");
}
AppendNativeCall(builder, method, invocation.Arguments);
builder.Append(method.WrapHandleReturn ? ");\n" : ";\n");
for (int index = fixedParameters.Count - 1; index >= 0; index--)
{
indentation -= 4;
builder.Append(' ', indentation);
builder.Append("}\n");
}
}
private static void AppendPinnedSpanInvocation(
StringBuilder builder,
ManagedMethodDefinition method,
PinnedSpanInvocation invocation
)
{
ManagedParameterDefinition span = method.Parameters[invocation.SpanParameterIndex];
builder.Append(" fixed (");
builder.Append(invocation.ElementType);
builder.Append("* __value = ");
builder.Append(EscapeIdentifier(span.Name));
builder.Append(")\n");
builder.Append(" {\n");
builder.Append(" __context->");
builder.Append(method.NativeCommand.Name);
builder.Append('(');
AppendArguments(builder, method, invocation.PrefixArguments);
if (invocation.PrefixArguments.Count != 0)
{
builder.Append(", ");
}
builder.Append(EscapeIdentifier(span.Name));
builder.Append(".Length, __value);\n");
builder.Append(" }\n");
}
private static void AppendPinnedVectorSpanInvocation(
StringBuilder builder,
ManagedMethodDefinition method,
PinnedVectorSpanInvocation invocation
)
{
ManagedParameterDefinition span = method.Parameters[invocation.SpanParameterIndex];
string spanName = EscapeIdentifier(span.Name);
builder.Append(" fixed (");
builder.Append(invocation.VectorType);
builder.Append("* __vector = ");
builder.Append(spanName);
builder.Append(")\n");
builder.Append(" {\n");
builder.Append(" __context->");
builder.Append(method.NativeCommand.Name);
builder.Append('(');
AppendArguments(builder, method, invocation.PrefixArguments);
if (invocation.PrefixArguments.Count != 0)
{
builder.Append(", ");
}
builder.Append(spanName);
builder.Append(".Length, (");
builder.Append(invocation.ComponentType);
builder.Append("*)__vector);\n");
builder.Append(" }\n");
}
private static void AppendMatrix4Invocation(
StringBuilder builder,
ManagedMethodDefinition method,
Matrix4Invocation invocation
)
{
ManagedParameterDefinition transpose = method.Parameters[invocation.TransposeParameterIndex];
ManagedParameterDefinition matrix = method.Parameters[invocation.MatrixParameterIndex];
string transposeName = EscapeIdentifier(transpose.Name);
string matrixName = EscapeIdentifier(matrix.Name);
if (invocation.IsSpan)
{
builder.Append(" fixed (GraphicsMatrix* __value = ");
builder.Append(matrixName);
builder.Append(")\n");
builder.Append(" {\n");
builder.Append(" __context->");
}
else
{
builder.Append(" __context->");
}
builder.Append(method.NativeCommand.Name);
builder.Append('(');
AppendArguments(builder, method, invocation.PrefixArguments);
if (invocation.PrefixArguments.Count != 0)
{
builder.Append(", ");
}
builder.Append(invocation.IsSpan ? $"{matrixName}.Length" : "1");
builder.Append(", ");
builder.Append(transposeName);
builder.Append(" ? (byte)1 : (byte)0, (float*)");
builder.Append(invocation.IsSpan ? "__value" : $"&{matrixName}");
builder.Append(");\n");
if (invocation.IsSpan)
{
builder.Append(" }\n");
}
}
private static void AppendNativeCall(
StringBuilder builder,
ManagedMethodDefinition method,
IReadOnlyList<ManagedArgument> arguments
)
{
builder.Append("__context->");
builder.Append(method.NativeCommand.Name);
builder.Append('(');
AppendArguments(builder, method, arguments);
builder.Append(')');
}
private static void AppendArguments(
StringBuilder builder,
ManagedMethodDefinition method,
IReadOnlyList<ManagedArgument> arguments
)
{
for (int index = 0; index < arguments.Count; index++)
{
if (index != 0)
{
builder.Append(", ");
}
switch (arguments[index])
{
case ParameterArgument parameterArgument:
ManagedParameterDefinition parameter = method.Parameters[
parameterArgument.ParameterIndex];
string parameterName = EscapeIdentifier(parameter.Name);
if (!parameter.IsHandle)
{
builder.Append(parameterName);
}
else if (parameter.Type.EndsWith('*'))
{
builder.Append("(uint*)");
builder.Append(parameterName);
}
else if (parameter.IsHandleSpan)
{
builder.Append("(uint*)__");
builder.Append(parameter.Name);
}
else
{
builder.Append(parameterName);
builder.Append(".Value");
}
break;
case SpanLengthArgument spanLengthArgument:
builder.Append(
EscapeIdentifier(
method.Parameters[spanLengthArgument.ParameterIndex].Name));
builder.Append(".Length");
break;
case VectorComponentArgument componentArgument:
builder.Append(
EscapeIdentifier(
method.Parameters[componentArgument.ParameterIndex].Name));
builder.Append('.');
builder.Append(componentArgument.Component);
break;
default:
throw new InvalidOperationException(
$"Unsupported managed argument '{arguments[index].GetType().Name}'.");
}
}
}
private static string EscapeIdentifier(
@@ -418,5 +733,16 @@ namespace StgSharp.GenerateGL.Generation
string Literal
);
private sealed record GlCommandGroup(
string FileName,
string Description,
IReadOnlyList<GlCommandDefinition> Commands
);
private sealed record ManagedCommandGroup(
GlCommandGroup Source,
IReadOnlyList<ManagedMethodDefinition> Methods
);
}
}