generate glenum, link glenum, add source gen for text regex

This commit is contained in:
Youmu Liu
2026-08-31 18:12:44 +08:00
parent 667b8d8e43
commit e52d0ac6f3
54 changed files with 4555 additions and 1596 deletions
@@ -0,0 +1,336 @@
// -----------------------------------------------------------------------------
// file="GlEnumProjector"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using StgSharp.GenerateGL.Naming;
using StgSharp.GenerateGL.Registry;
namespace StgSharp.GenerateGL.Generation
{
/// <summary>
/// Projects registry GLenum and GLbitfield parameter groups into C# enum types.
/// </summary>
internal static class GlEnumProjector
{
private static readonly string[] _coreExtensionSuffixes =
{
"ARB",
"EXT",
"KHR",
"NV",
};
private static readonly ReadOnlyDictionary<string, string> _managedTypeNameOverrides =
new ReadOnlyDictionary<string, string>(
new Dictionary<string, string>(StringComparer.Ordinal)
{
["Buffer"] = "ClearBuffer",
});
public static IReadOnlyList<ManagedEnumDefinition> Project(
GlRegistryProjection projection
)
{
ArgumentNullException.ThrowIfNull(projection);
Dictionary<string, bool> referencedGroups = ReadReferencedGroups(projection.Commands);
Dictionary<string, GlEnumDefinition> selectedEnums = projection.Enums
.Where(value => value.Value is not null)
.GroupBy(value => value.Name, StringComparer.Ordinal)
.ToDictionary(
value => value.Key,
value => value.First(),
StringComparer.Ordinal);
Dictionary<string, List<GlEnumDefinition>> membersByGroup =
new Dictionary<string, List<GlEnumDefinition>>(StringComparer.Ordinal);
foreach (GlEnumDefinition value in selectedEnums.Values)
{
foreach (string group in SplitGroups(value.Group))
{
if (!referencedGroups.ContainsKey(group))
{
continue;
}
if (!membersByGroup.TryGetValue(group, out List<GlEnumDefinition>? members))
{
members = new List<GlEnumDefinition>();
membersByGroup.Add(group, members);
}
members.Add(value);
}
}
Dictionary<string, EnumBuilder> enumsByManagedName =
new Dictionary<string, EnumBuilder>(StringComparer.Ordinal);
foreach ((string nativeGroup, bool isFlags) in referencedGroups.OrderBy(
value => value.Key,
StringComparer.Ordinal))
{
if (!membersByGroup.TryGetValue(nativeGroup, out List<GlEnumDefinition>? members) ||
members.Count == 0)
{
continue;
}
string managedName = ReadManagedTypeName(nativeGroup);
if (!enumsByManagedName.TryGetValue(managedName, out EnumBuilder? builder))
{
builder = new EnumBuilder(managedName);
enumsByManagedName.Add(managedName, builder);
}
builder.AddGroup(nativeGroup, isFlags, members);
}
return Array.AsReadOnly(
enumsByManagedName.Values
.OrderBy(value => value.Name, StringComparer.Ordinal)
.Select(value => value.Build())
.ToArray());
}
private static Dictionary<string, bool> ReadReferencedGroups(
IReadOnlyList<GlCommandDefinition> commands
)
{
Dictionary<string, bool> result = new Dictionary<string, bool>(
StringComparer.Ordinal);
foreach (GlCommandDefinition command in commands)
{
AddReferencedGroups(
result,
command.Return.Group,
command.Return.TypeName);
foreach (GlParameterDefinition parameter in command.Parameters)
{
AddReferencedGroups(result, parameter.Group, parameter.TypeName);
}
}
return result;
}
private static void AddReferencedGroups(
Dictionary<string, bool> result,
string? groupNames,
string? nativeType
)
{
if (groupNames is null || nativeType is not ("GLenum" or "GLbitfield"))
{
return;
}
bool isFlags = string.Equals(nativeType, "GLbitfield", StringComparison.Ordinal);
foreach (string group in SplitGroups(groupNames))
{
result[group] = result.TryGetValue(group, out bool previous)
? previous || isFlags
: isFlags;
}
}
private static IEnumerable<string> SplitGroups(
string? value
)
{
if (string.IsNullOrWhiteSpace(value))
{
yield break;
}
foreach (string item in value.Split(','))
{
string group = item.Trim();
if (group.Length != 0)
{
yield return group;
}
}
}
private static string ReadManagedTypeName(
string nativeGroup
)
{
if (_managedTypeNameOverrides.TryGetValue(nativeGroup, out string? overrideName))
{
return overrideName;
}
string value = nativeGroup;
foreach (string suffix in _coreExtensionSuffixes)
{
if (value.Length > suffix.Length && value.EndsWith(
suffix,
StringComparison.Ordinal))
{
value = value[..^suffix.Length];
break;
}
}
return CSharpIdentifierPolicy.NormalizeMemberName(SanitizeIdentifier(value));
}
private static string ReadManagedMemberName(
string nativeName
)
{
string value = nativeName.StartsWith("GL_", StringComparison.Ordinal)
? nativeName[3..]
: nativeName;
StringBuilder builder = new StringBuilder(value.Length);
foreach (string component in value.Split('_', StringSplitOptions.RemoveEmptyEntries))
{
if (component.Length == 0)
{
continue;
}
if (char.IsLetter(component[0]))
{
builder.Append(char.ToUpperInvariant(component[0]));
if (component.Length > 1)
{
foreach (char character in component.AsSpan(1))
{
builder.Append(ToAsciiLowerInvariant(character));
}
}
}
else
{
builder.Append(component);
}
}
string result = builder.Length == 0 ? "Value" : builder.ToString();
return CSharpIdentifierPolicy.NormalizeMemberName(SanitizeIdentifier(result));
}
private static char ToAsciiLowerInvariant(
char value
)
{
return value is >= 'A' and <= 'Z' ? (char)(value + ('a' - 'A')) : value;
}
private static string SanitizeIdentifier(
string value
)
{
StringBuilder builder = new StringBuilder(value.Length + 1);
if (value.Length == 0 || !char.IsLetter(value[0]) && value[0] != '_')
{
builder.Append('_');
}
foreach (char character in value)
{
builder.Append(char.IsLetterOrDigit(character) || character == '_'
? character
: '_');
}
return builder.ToString();
}
private sealed class EnumBuilder
{
private readonly Dictionary<string, ManagedEnumMemberDefinition> _members =
new Dictionary<string, ManagedEnumMemberDefinition>(StringComparer.Ordinal);
private readonly HashSet<string> _nativeGroups = new HashSet<string>(
StringComparer.Ordinal);
public EnumBuilder(
string name
)
{
Name = name;
}
public bool IsFlags { get; private set; }
public string Name { get; }
public void AddGroup(
string nativeGroup,
bool isFlags,
IEnumerable<GlEnumDefinition> members
)
{
_nativeGroups.Add(nativeGroup);
IsFlags |= isFlags;
foreach (GlEnumDefinition value in members)
{
string name = ReadManagedMemberName(value.Name);
if (string.Equals(name, Name, StringComparison.Ordinal))
{
name = $"{name}Value";
}
if (_members.TryGetValue(name, out ManagedEnumMemberDefinition? previous))
{
if (!string.Equals(
previous.NativeValue.Value,
value.Value,
StringComparison.Ordinal))
{
throw new InvalidDataException(
$"Enum member '{Name}.{name}' maps to conflicting native values " +
$"'{previous.NativeValue.Name}' and '{value.Name}'.");
}
continue;
}
_members.Add(name, new ManagedEnumMemberDefinition(name, value));
}
}
public ManagedEnumDefinition Build()
{
List<ManagedEnumMemberDefinition> members = _members.Values
.OrderBy(value => value.Name, StringComparer.Ordinal)
.ToList();
if (IsFlags && members.All(value => value.NativeValue.Value != "0" &&
value.NativeValue.Value != "0x0" &&
value.NativeValue.Value != "0x00000000"))
{
members.Insert(
0,
new ManagedEnumMemberDefinition(
"None",
new GlEnumDefinition(
"GL_NONE",
"0",
null,
null,
string.Join(",", _nativeGroups),
null,
"Synthetic zero value for a generated flags enum.",
members[0].NativeValue.Element)));
}
return new ManagedEnumDefinition(
Name,
IsFlags,
Array.AsReadOnly(
_nativeGroups.OrderBy(value => value, StringComparer.Ordinal).ToArray()),
Array.AsReadOnly(members.ToArray()));
}
}
}
}
@@ -0,0 +1,122 @@
// -----------------------------------------------------------------------------
// file="GlEnumTypeMap"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using StgSharp.GenerateGL.Registry;
namespace StgSharp.GenerateGL.Generation
{
/// <summary>
/// Resolves unambiguous scalar GLenum and GLbitfield groups to generated C# types.
/// </summary>
internal sealed class GlEnumTypeMap
{
private readonly Dictionary<string, string> _typesByNativeGroup;
public GlEnumTypeMap(
IEnumerable<ManagedEnumDefinition> definitions
)
{
ArgumentNullException.ThrowIfNull(definitions);
_typesByNativeGroup = new Dictionary<string, string>(StringComparer.Ordinal);
foreach (ManagedEnumDefinition definition in definitions)
{
foreach (string nativeGroup in definition.NativeGroups)
{
if (_typesByNativeGroup.TryGetValue(nativeGroup, out string? previous) &&
!string.Equals(previous, definition.Name, StringComparison.Ordinal))
{
throw new InvalidOperationException(
$"Native enum group '{nativeGroup}' maps to both " +
$"'{previous}' and '{definition.Name}'.");
}
_typesByNativeGroup[nativeGroup] = definition.Name;
}
}
}
public bool TryReadParameterType(
GlParameterDefinition parameter,
out string managedType
)
{
ArgumentNullException.ThrowIfNull(parameter);
if (!IsEnumDeclaration(parameter.TypeName, parameter.Group))
{
managedType = string.Empty;
return false;
}
return TryReadGroupType(parameter.Group!, out managedType);
}
public bool TryReadReturnType(
GlReturnDefinition value,
out string managedType
)
{
ArgumentNullException.ThrowIfNull(value);
if (!IsEnumDeclaration(value.TypeName, value.Group))
{
managedType = string.Empty;
return false;
}
return TryReadGroupType(value.Group!, out managedType);
}
private static bool IsEnumDeclaration(
string? nativeType,
string? group
)
{
return group is not null &&
nativeType is "GLenum" or "GLbitfield";
}
private bool TryReadGroupType(
string groups,
out string managedType
)
{
string[] nativeGroups = groups.Split(',')
.Select(value => value.Trim())
.Where(value => value.Length != 0)
.ToArray();
if (nativeGroups.Length == 0)
{
managedType = string.Empty;
return false;
}
HashSet<string> candidates = new HashSet<string>(StringComparer.Ordinal);
foreach (string nativeGroup in nativeGroups)
{
if (!_typesByNativeGroup.TryGetValue(nativeGroup, out string? candidate))
{
managedType = string.Empty;
return false;
}
candidates.Add(candidate);
}
if (candidates.Count == 1)
{
managedType = candidates.First();
return true;
}
managedType = string.Empty;
return false;
}
}
}
@@ -0,0 +1,20 @@
// -----------------------------------------------------------------------------
// file="GlGenerationTarget"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
namespace StgSharp.GenerateGL.Generation
{
[Flags]
internal enum GlGenerationTarget
{
None = 0,
Api = 1,
Enum = 2,
All = Api | Enum,
}
}
@@ -0,0 +1,44 @@
// -----------------------------------------------------------------------------
// file="GlManagedApiSuppressionFilter"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using StgSharp.GenerateGL.Registry;
namespace StgSharp.GenerateGL.Generation
{
/// <summary>
/// Omits raw managed forwards for commands that have safer handwritten public APIs.
/// </summary>
internal sealed class GlManagedApiSuppressionFilter : IGlApiFilter
{
private static readonly HashSet<string> _suppressedCommands = new HashSet<string>(
new[]
{
"glCreateShader",
"glShaderSource",
},
StringComparer.Ordinal);
public IReadOnlyList<ManagedMethodDefinition> Apply(
GlCommandDefinition command,
IReadOnlyList<ManagedMethodDefinition> methods,
GlEquivalentTypeMapper typeMapper
)
{
ArgumentNullException.ThrowIfNull(command);
ArgumentNullException.ThrowIfNull(methods);
ArgumentNullException.ThrowIfNull(typeMapper);
return _suppressedCommands.Contains(command.Name)
? Array.Empty<ManagedMethodDefinition>()
: methods;
}
}
}
@@ -17,6 +17,7 @@ namespace StgSharp.GenerateGL.Generation
GlCommandDefinition NativeCommand,
ManagedInvocation Invocation,
bool WrapHandleReturn = false,
bool IsEnumReturn = false,
string? DocumentationPage = null
);
@@ -25,7 +26,8 @@ namespace StgSharp.GenerateGL.Generation
string Name,
GlParameterDefinition? NativeParameter = null,
bool IsHandle = false,
bool IsHandleSpan = false
bool IsHandleSpan = false,
bool IsEnum = false
);
internal abstract record ManagedInvocation;
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using StgSharp.GenerateGL.Analysis;
using StgSharp.GenerateGL.Naming;
@@ -20,21 +21,26 @@ namespace StgSharp.GenerateGL.Generation
private readonly IReadOnlyList<IGlApiFilter> _filters = Array.AsReadOnly<IGlApiFilter>(
new IGlApiFilter[]
{
new GlManagedApiSuppressionFilter(),
new GlOverloadApiFilter(),
new GlGetValueApiFilter(),
new GlHandleApiFilter(),
});
private readonly GlEquivalentTypeMapper _typeMapper;
private readonly GlEnumTypeMap _enumTypeMap;
private readonly GlManagedApiNamePolicy _namePolicy;
public ManagedApiPlanner(
GlEquivalentTypeMapper typeMapper,
GlFunctionFamilyAnalysis familyAnalysis
GlFunctionFamilyAnalysis familyAnalysis,
GlEnumTypeMap enumTypeMap
)
{
ArgumentNullException.ThrowIfNull(typeMapper);
ArgumentNullException.ThrowIfNull(familyAnalysis);
ArgumentNullException.ThrowIfNull(enumTypeMap);
_typeMapper = typeMapper;
_enumTypeMap = enumTypeMap;
_namePolicy = new GlManagedApiNamePolicy(familyAnalysis);
}
@@ -57,6 +63,8 @@ namespace StgSharp.GenerateGL.Generation
methods = filter.Apply(command, methods, _typeMapper);
}
methods = ApplyEnumTypes(command, methods);
result.AddRange(
methods.Select(
method => method with
@@ -77,6 +85,77 @@ namespace StgSharp.GenerateGL.Generation
return Array.AsReadOnly(result.ToArray());
}
private ReadOnlyCollection<ManagedMethodDefinition> ApplyEnumTypes(
GlCommandDefinition command,
IReadOnlyList<ManagedMethodDefinition> methods
)
{
bool hasEnumReturn = _enumTypeMap.TryReadReturnType(
command.Return,
out string enumReturnType);
return Array.AsReadOnly(
methods.Select(
method => ConvertEnumReturn(method, hasEnumReturn, enumReturnType)).ToArray());
}
private ManagedMethodDefinition ConvertEnumReturn(
ManagedMethodDefinition method,
bool hasEnumReturn,
string enumReturnType
)
{
string returnType = string.Empty;
bool isEnumReturn = hasEnumReturn && TryReplaceNativeEnumType(
method.ReturnType,
enumReturnType,
out returnType);
return method with
{
ReturnType = isEnumReturn ? returnType : method.ReturnType,
IsEnumReturn = isEnumReturn,
Parameters = Array.AsReadOnly(
method.Parameters.Select(ConvertEnumParameter).ToArray()),
};
}
private ManagedParameterDefinition ConvertEnumParameter(
ManagedParameterDefinition parameter
)
{
if (parameter.NativeParameter is null ||
!_enumTypeMap.TryReadParameterType(
parameter.NativeParameter,
out string enumType) ||
!TryReplaceNativeEnumType(parameter.Type, enumType, out string convertedType))
{
return parameter;
}
return parameter with
{
Type = convertedType,
IsEnum = true,
};
}
private static bool TryReplaceNativeEnumType(
string nativeType,
string enumType,
out string convertedType
)
{
const string nativeBaseType = "uint";
if (!nativeType.StartsWith(nativeBaseType, StringComparison.Ordinal) ||
nativeType.AsSpan(nativeBaseType.Length).ContainsAnyExcept('*'))
{
convertedType = string.Empty;
return false;
}
convertedType = enumType + nativeType[nativeBaseType.Length..];
return true;
}
public static void Validate(
IEnumerable<ManagedMethodDefinition> methods
)
@@ -0,0 +1,24 @@
// -----------------------------------------------------------------------------
// file="ManagedEnumModel"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
using StgSharp.GenerateGL.Registry;
namespace StgSharp.GenerateGL.Generation
{
internal sealed record ManagedEnumDefinition(
string Name,
bool IsFlags,
IReadOnlyList<string> NativeGroups,
IReadOnlyList<ManagedEnumMemberDefinition> Members
);
internal sealed record ManagedEnumMemberDefinition(
string Name,
GlEnumDefinition NativeValue
);
}
@@ -42,59 +42,76 @@ namespace StgSharp.GenerateGL.Generation
StringComparer.Ordinal);
public static GeneratedFileSet Generate(
GlRegistryProjection projection
GlRegistryProjection projection,
GlGenerationTarget target = GlGenerationTarget.All
)
{
ArgumentNullException.ThrowIfNull(projection);
if (target == GlGenerationTarget.None || (target & ~GlGenerationTarget.All) != 0)
{
throw new ArgumentOutOfRangeException(nameof(target));
}
IReadOnlyList<GlCommandGroup> commandGroups = ReadCoreCommandGroups(projection);
IReadOnlyList<GlCommandDefinition> commands = Array.AsReadOnly(
commandGroups.SelectMany(value => value.Commands).ToArray());
GlEquivalentTypeMapper typeMapper = new GlEquivalentTypeMapper(projection.Source);
GlFunctionFamilyAnalysis familyAnalysis = GlFunctionFamilyAnalyzer.Analyze(projection);
ManagedApiPlanner apiPlanner = new ManagedApiPlanner(typeMapper, familyAnalysis);
IReadOnlyList<ManagedMethodDefinition> managedMethods = apiPlanner.Plan(commands);
Dictionary<string, IReadOnlyList<ManagedMethodDefinition>> methodsByCommand =
managedMethods.GroupBy(value => value.NativeCommand.Name, StringComparer.Ordinal)
.ToDictionary(
value => value.Key,
value => (IReadOnlyList<ManagedMethodDefinition>)Array.AsReadOnly(
value.ToArray()),
StringComparer.Ordinal);
List<ManagedCommandGroup> managedGroups = commandGroups.Select(
group => new ManagedCommandGroup(
group,
Array.AsReadOnly(
group.Commands.SelectMany(
command => methodsByCommand.TryGetValue(
command.Name,
out IReadOnlyList<ManagedMethodDefinition>? methods)
? methods
: Array.Empty<ManagedMethodDefinition>())
.ToArray()))).ToList();
ManagedApiPlanner.Validate(
managedGroups.SelectMany(value => value.Methods));
List<GeneratedFile> files = new List<GeneratedFile>
IReadOnlyList<ManagedEnumDefinition> enums = GlEnumProjector.Project(projection);
GlEnumTypeMap enumTypeMap = new GlEnumTypeMap(enums);
List<GeneratedFile> files = new List<GeneratedFile>();
if ((target & GlGenerationTarget.Enum) != 0)
{
new GeneratedFile("glconst.cs", GenerateConstants(projection.Enums)),
new GeneratedFile(
files.Add(new GeneratedFile("glconst.cs", GenerateConstants(projection.Enums)));
files.Add(new GeneratedFile("GlEnums.g.cs", GenerateEnums(enums)));
}
if ((target & GlGenerationTarget.Api) != 0)
{
IReadOnlyList<GlCommandGroup> commandGroups = ReadCoreCommandGroups(projection);
IReadOnlyList<GlCommandDefinition> commands = Array.AsReadOnly(
commandGroups.SelectMany(value => value.Commands).ToArray());
GlEquivalentTypeMapper typeMapper = new GlEquivalentTypeMapper(projection.Source);
GlFunctionFamilyAnalysis familyAnalysis = GlFunctionFamilyAnalyzer.Analyze(projection);
ManagedApiPlanner apiPlanner = new ManagedApiPlanner(
typeMapper,
familyAnalysis,
enumTypeMap);
IReadOnlyList<ManagedMethodDefinition> managedMethods = apiPlanner.Plan(commands);
Dictionary<string, IReadOnlyList<ManagedMethodDefinition>> methodsByCommand =
managedMethods.GroupBy(value => value.NativeCommand.Name, StringComparer.Ordinal)
.ToDictionary(
value => value.Key,
value => (IReadOnlyList<ManagedMethodDefinition>)Array.AsReadOnly(
value.ToArray()),
StringComparer.Ordinal);
List<ManagedCommandGroup> managedGroups = commandGroups.Select(
group => new ManagedCommandGroup(
group,
Array.AsReadOnly(
group.Commands.SelectMany(
command => methodsByCommand.TryGetValue(
command.Name,
out IReadOnlyList<ManagedMethodDefinition>? methods)
? methods
: Array.Empty<ManagedMethodDefinition>())
.ToArray()))).ToList();
ManagedApiPlanner.Validate(
managedGroups.SelectMany(value => value.Methods));
files.Add(new GeneratedFile(
"OpenglContext.g.cs",
GenerateContext(commands, typeMapper)),
new GeneratedFile(
GenerateContext(commands, typeMapper)));
files.Add(new GeneratedFile(
"glContextShadow.g.cs",
GenerateContextShadow(commands)),
};
for (int index = 0; index < managedGroups.Count; index++)
{
ManagedCommandGroup managedGroup = managedGroups[index];
GlCommandGroup group = managedGroup.Source;
files.Add(
new GeneratedFile(
group.FileName,
GenerateFunctions(
managedGroup.Methods,
group.Description,
index == 0)));
GenerateContextShadow(commands)));
for (int index = 0; index < managedGroups.Count; index++)
{
ManagedCommandGroup managedGroup = managedGroups[index];
GlCommandGroup group = managedGroup.Source;
files.Add(
new GeneratedFile(
group.FileName,
GenerateFunctions(
managedGroup.Methods,
group.FileName,
group.Description,
index == 0)));
}
}
return new GeneratedFileSet(files);
@@ -184,7 +201,7 @@ namespace StgSharp.GenerateGL.Generation
)
{
StringBuilder builder = new StringBuilder(512 * 1024);
AppendGeneratedHeader(builder, "Desktop OpenGL constants");
AppendGeneratedHeader(builder, "glconst.cs", "Desktop OpenGL constants");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
builder.Append("{\n");
builder.Append(" public static class glConst\n");
@@ -227,6 +244,60 @@ namespace StgSharp.GenerateGL.Generation
return builder.ToString();
}
private static string GenerateEnums(
IReadOnlyList<ManagedEnumDefinition> enums
)
{
StringBuilder builder = new StringBuilder(512 * 1024);
AppendGeneratedHeader(
builder,
"GlEnums.g.cs",
"Strongly typed desktop OpenGL enum groups");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
builder.Append("{\n");
foreach (ManagedEnumDefinition value in enums)
{
builder.Append(" /// <summary>Native OpenGL group");
builder.Append(value.NativeGroups.Count == 1 ? " " : "s ");
builder.Append(string.Join(", ", value.NativeGroups));
builder.Append(".</summary>\n");
if (value.IsFlags)
{
builder.Append(" [System.Flags]\n");
}
builder.Append(" public enum ");
builder.Append(value.Name);
builder.Append(" : uint\n");
builder.Append(" {\n");
foreach (ManagedEnumMemberDefinition member in value.Members)
{
builder.Append(" ");
builder.Append(member.Name);
builder.Append(" = ");
if (member.NativeValue.Name == "GL_NONE" &&
member.NativeValue.Comment ==
"Synthetic zero value for a generated flags enum.")
{
builder.Append("0u");
}
else
{
builder.Append("unchecked((uint)glConst.");
builder.Append(ReadConstantName(member.NativeValue.Name));
builder.Append(')');
}
builder.Append(",\n");
}
builder.Append(" }\n\n");
}
builder.Append("}\n");
return builder.ToString();
}
private static ConstantExpression ReadConstantExpression(
GlEnumDefinition value
)
@@ -329,7 +400,10 @@ namespace StgSharp.GenerateGL.Generation
)
{
StringBuilder builder = new StringBuilder(128 * 1024);
AppendGeneratedHeader(builder, "Desktop OpenGL core function pointer table");
AppendGeneratedHeader(
builder,
"OpenglContext.g.cs",
"Desktop OpenGL core function pointer table");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
builder.Append("{\n");
builder.Append(" [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]\n");
@@ -360,7 +434,10 @@ namespace StgSharp.GenerateGL.Generation
)
{
StringBuilder builder = new StringBuilder(64 * 1024);
AppendGeneratedHeader(builder, "Native-address shadow of the OpenGL function pointer table");
AppendGeneratedHeader(
builder,
"glContextShadow.g.cs",
"Native-address shadow of the OpenGL function pointer table");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
builder.Append("{\n");
builder.Append(" [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]\n");
@@ -389,13 +466,14 @@ namespace StgSharp.GenerateGL.Generation
private static string GenerateFunctions(
IReadOnlyList<ManagedMethodDefinition> methods,
string outputFileName,
string description,
bool includeContextState
)
{
StringBuilder builder = new StringBuilder(512 * 1024);
AppendGeneratedHeader(builder, description);
builder.Append("using StgSharp.Mathematics.Graphics;\n\n");
AppendGeneratedHeader(builder, outputFileName, description);
builder.Append("using StgSharp.Mathematics.Numeric.Graphics;\n\n");
builder.Append("using System;\n");
builder.Append("using System.Runtime.CompilerServices;\n\n");
builder.Append("namespace StgSharp.Graphics.OpenGL\n");
@@ -572,6 +650,12 @@ namespace StgSharp.GenerateGL.Generation
{
builder.Append("return GlHandle.Create(");
}
else if (method.IsEnumReturn)
{
builder.Append("return (");
builder.Append(method.ReturnType);
builder.Append(')');
}
else if (method.ReturnType != "void")
{
builder.Append("return ");
@@ -719,7 +803,14 @@ namespace StgSharp.GenerateGL.Generation
ManagedParameterDefinition parameter = method.Parameters[
parameterArgument.ParameterIndex];
string parameterName = EscapeIdentifier(parameter.Name);
if (!parameter.IsHandle)
if (parameter.IsEnum)
{
builder.Append("(uint");
builder.Append('*', parameter.Type.Count(value => value == '*'));
builder.Append(')');
builder.Append(parameterName);
}
else if (!parameter.IsHandle)
{
builder.Append(parameterName);
}
@@ -769,9 +860,18 @@ namespace StgSharp.GenerateGL.Generation
private static void AppendGeneratedHeader(
StringBuilder builder,
string outputFileName,
string description
)
{
builder.Append("// -----------------------------------------------------------------------------\n");
builder.Append("// file=\"");
builder.Append(ReadGeneratedFileName(outputFileName));
builder.Append("\"\n");
builder.Append("// Project: StgSharp\n");
builder.Append("// Copyright (c) Nitload.\n");
builder.Append("// SPDX-License-Identifier: MIT\n");
builder.Append("// -----------------------------------------------------------------------------\n\n");
builder.Append("// <auto-generated>\n");
builder.Append("// Generated by StgSharp.GenerateGL.\n");
builder.Append("// ");
@@ -780,6 +880,22 @@ namespace StgSharp.GenerateGL.Generation
builder.Append("// </auto-generated>\n\n");
}
private static string ReadGeneratedFileName(
string outputFileName
)
{
const string generatedExtension = ".g.cs";
const string sourceExtension = ".cs";
if (outputFileName.EndsWith(generatedExtension, StringComparison.Ordinal))
{
return outputFileName[..^generatedExtension.Length];
}
return outputFileName.EndsWith(sourceExtension, StringComparison.Ordinal)
? outputFileName[..^sourceExtension.Length]
: outputFileName;
}
private sealed record ConstantExpression(
string Type,
string Literal