make new opengl source generator
This commit is contained in:
@@ -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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user