generate glenum, link glenum, add source gen for text regex
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ This directory contains repository-maintenance and source-generation programs. T
|
||||
`StgSharp.GenerateGL` has its own `README.md` with its pipeline, naming rules, and interactive commands. The common direct-generation command is:
|
||||
|
||||
```powershell
|
||||
dotnet run --project tools/StgSharp.GenerateGL/StgSharp.GenerateGL.csproj -- generate src/StgSharp.Graphics/OpenGL/Generated
|
||||
dotnet run --project tools/StgSharp.GenerateGL/StgSharp.GenerateGL.csproj -- generate all src/StgSharp.Graphics/OpenGL/Generated
|
||||
```
|
||||
|
||||
Generated files begin with `// <auto-generated>` and should not be edited manually. Change the registry projection, analysis, naming, or generation code here and regenerate the destination instead.
|
||||
|
||||
@@ -42,18 +42,20 @@ namespace StgSharp.GenerateGL
|
||||
return SuccessExitCode;
|
||||
}
|
||||
|
||||
if (arguments.Count > 1)
|
||||
if (arguments.Count == 0 || arguments.Count > 2 ||
|
||||
!TryReadTarget(arguments[0], out GlGenerationTarget target))
|
||||
{
|
||||
Console.Error.WriteLine("error: generate accepts at most one output directory.");
|
||||
Console.Error.WriteLine(
|
||||
"error: generate requires api, enum, or all and accepts one optional output directory.");
|
||||
Console.Error.WriteLine("Run 'generate --help' for command information.");
|
||||
return UsageErrorExitCode;
|
||||
}
|
||||
|
||||
string? outputDirectory = arguments.Count == 1 ? arguments[0] : null;
|
||||
string? outputDirectory = arguments.Count == 2 ? arguments[1] : null;
|
||||
string? resolvedOutputDirectory = null;
|
||||
try
|
||||
{
|
||||
generatedFiles = OpenGlGenerator.Generate(projection);
|
||||
generatedFiles = OpenGlGenerator.Generate(projection, target);
|
||||
if (outputDirectory is not null)
|
||||
{
|
||||
resolvedOutputDirectory = WriteGeneratedFiles(
|
||||
@@ -103,6 +105,21 @@ namespace StgSharp.GenerateGL
|
||||
return SuccessExitCode;
|
||||
}
|
||||
|
||||
private static bool TryReadTarget(
|
||||
string value,
|
||||
out GlGenerationTarget target
|
||||
)
|
||||
{
|
||||
target = value.ToUpperInvariant() switch
|
||||
{
|
||||
"API" => GlGenerationTarget.Api,
|
||||
"ENUM" or "ENUMS" => GlGenerationTarget.Enum,
|
||||
"ALL" => GlGenerationTarget.All,
|
||||
_ => GlGenerationTarget.None,
|
||||
};
|
||||
return target != GlGenerationTarget.None;
|
||||
}
|
||||
|
||||
private static string WriteGeneratedFiles(
|
||||
GeneratedFileSet files,
|
||||
string outputDirectory
|
||||
@@ -217,7 +234,7 @@ namespace StgSharp.GenerateGL
|
||||
if (files.Files.Count == 0)
|
||||
{
|
||||
Console.Error.WriteLine("No generated files are held in this session.");
|
||||
Console.Error.WriteLine("Run 'generate' first.");
|
||||
Console.Error.WriteLine("Run 'generate api', 'generate enum', or 'generate all' first.");
|
||||
return QueryErrorExitCode;
|
||||
}
|
||||
|
||||
@@ -251,7 +268,8 @@ namespace StgSharp.GenerateGL
|
||||
$"No generated file named '{arguments[0]}' is held in this session.");
|
||||
if (files.Files.Count == 0)
|
||||
{
|
||||
Console.Error.WriteLine("Run 'generate' first.");
|
||||
Console.Error.WriteLine(
|
||||
"Run 'generate api', 'generate enum', or 'generate all' first.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -271,12 +289,19 @@ namespace StgSharp.GenerateGL
|
||||
Console.WriteLine("Generate C# bindings from the loaded OpenGL registry.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine(" generate");
|
||||
Console.WriteLine(" generate <output-directory>");
|
||||
Console.WriteLine(" generate api [output-directory]");
|
||||
Console.WriteLine(" generate enum [output-directory]");
|
||||
Console.WriteLine(" generate all [output-directory]");
|
||||
Console.WriteLine(" generate --help");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Targets:");
|
||||
Console.WriteLine(" api Generate function tables and managed forwarding APIs.");
|
||||
Console.WriteLine(" enum Generate glConst and strongly typed enum groups.");
|
||||
Console.WriteLine(" all Generate both API and enum files.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Output:");
|
||||
Console.WriteLine(" Without a directory, output is retained in memory only.");
|
||||
Console.WriteLine(" API output references enum output; use all for a clean directory.");
|
||||
Console.WriteLine(" The output directory must already exist and be writable.");
|
||||
Console.WriteLine(" Existing files are replaced only when marked as generated.");
|
||||
Console.WriteLine();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,7 +21,7 @@ show command glBindBuffer
|
||||
target summary
|
||||
families list Vertex
|
||||
families show VertexAttributeP
|
||||
generate
|
||||
generate all
|
||||
list files
|
||||
preview OpenGLFunction.v4_1.g.cs
|
||||
```
|
||||
@@ -29,9 +29,21 @@ preview OpenGLFunction.v4_1.g.cs
|
||||
Generate directly into an existing directory:
|
||||
|
||||
```powershell
|
||||
dotnet run --project tools/StgSharp.GenerateGL/StgSharp.GenerateGL.csproj -- generate src/StgSharp.Graphics/OpenGL/Generated
|
||||
dotnet run --project tools/StgSharp.GenerateGL/StgSharp.GenerateGL.csproj -- generate all src/StgSharp.Graphics/OpenGL/Generated
|
||||
```
|
||||
|
||||
Generation targets are explicit:
|
||||
|
||||
```text
|
||||
generate api [output-directory] # function tables and managed forwarding APIs
|
||||
generate enum [output-directory] # glConst and strongly typed enum groups
|
||||
generate all [output-directory] # both sets
|
||||
```
|
||||
|
||||
The API files reference the generated enum types. Use `generate all` for a clean output
|
||||
directory; use `generate api` when refreshing an output tree that already contains the enum
|
||||
files.
|
||||
|
||||
Without a directory, `generate` retains its output in the current CLI session. With a directory:
|
||||
|
||||
- the directory must already exist and be writable;
|
||||
@@ -47,6 +59,7 @@ Use a directory dedicated to generated source files.
|
||||
gl.xml
|
||||
-> lossless XML and registry models
|
||||
-> desktop OpenGL 4.6 Core projection
|
||||
-> GLenum/GLbitfield semantic group projection
|
||||
-> function-family and scalar/vector relation analysis
|
||||
-> managed API filters
|
||||
-> C# naming and signature validation
|
||||
@@ -59,6 +72,7 @@ The target projection removes GLES and ARB/NV/EXT extension entry points. It ret
|
||||
Generated files:
|
||||
|
||||
- `glconst.cs`: core constants;
|
||||
- `GlEnums.g.cs`: strongly typed enum groups referenced by Core Profile parameters and return values;
|
||||
- `OpenglContext.g.cs`: the function-pointer table with native names and ABI types;
|
||||
- `glContextShadow.g.cs`: an identically ordered `nint` table used while loading native entry points;
|
||||
- `OpenGLFunction.Base.g.cs`: APIs introduced through OpenGL 3.2 and retained in the target profile;
|
||||
@@ -134,6 +148,10 @@ Renaming never changes the native target. For example, `VertexAttributeInteger(.
|
||||
## Type rules
|
||||
|
||||
- `gl.xml` typedefs map to equivalent C# ABI types; the generator does not emit separate type aliases;
|
||||
- `GLenum` and `GLbitfield` declarations with one unambiguous registry group use the generated enum type in public APIs;
|
||||
- enum pointers preserve their pointer shape (for example, `GLenum*` becomes `DebugSource*`) and are cast back to the native `uint*` representation at the forwarding boundary;
|
||||
- enum return values are converted from the native `uint` representation before being returned;
|
||||
- declarations with missing or conflicting enum group metadata remain on their ABI type;
|
||||
- a `GLuint` is converted to `GlHandle` only when object class and parameter semantics identify it as an object handle;
|
||||
- handle arrays use `Span<GlHandle>` or `ReadOnlySpan<GlHandle>`;
|
||||
- `OpenGLFunction` stores an `OpenglContext*` and forwards through `__context->glFunction(...)`;
|
||||
@@ -141,6 +159,8 @@ Renaming never changes the native target. For example, `VertexAttributeInteger(.
|
||||
|
||||
A `T*` alone does not determine whether a managed API should use `Span<T>`, `ReadOnlySpan<T>`, `out T`, or a return value. Future pointer improvements should be implemented as family-specific filters validated with `len`, parameter direction, and OpenGL state semantics.
|
||||
|
||||
Raw managed forwards are omitted when a safer handwritten public API owns the command. In particular, `glShaderSource` remains in `OpenglContext` for ABI loading, while `OpenGLFunction` exposes the handwritten `ReadOnlySpan<byte>` and `string` overloads instead of a generated `byte**` signature.
|
||||
|
||||
## Documentation comments
|
||||
|
||||
Every generated public method contains:
|
||||
|
||||
@@ -1437,7 +1437,7 @@ namespace StgSharp.GenerateGL
|
||||
Console.WriteLine(" StgSharp.GenerateGL [--xml <path>] show <category> <name> " +
|
||||
"[--showcount <n>]");
|
||||
Console.WriteLine(
|
||||
" StgSharp.GenerateGL [--xml <path>] generate [output-directory]");
|
||||
" StgSharp.GenerateGL [--xml <path>] generate <api|enum|all> [output-directory]");
|
||||
Console.WriteLine(" StgSharp.GenerateGL [--xml <path>] target summary");
|
||||
Console.WriteLine(" StgSharp.GenerateGL [--xml <path>] families <command>");
|
||||
Console.WriteLine(" StgSharp.GenerateGL [--xml <path>] list files");
|
||||
@@ -1456,9 +1456,9 @@ namespace StgSharp.GenerateGL
|
||||
Console.WriteLine(" StgSharp.GenerateGL show extension GL_KHR_debug");
|
||||
Console.WriteLine(" StgSharp.GenerateGL target summary");
|
||||
Console.WriteLine(" StgSharp.GenerateGL families show VertexAttribP");
|
||||
Console.WriteLine(" StgSharp.GenerateGL generate");
|
||||
Console.WriteLine(" StgSharp.GenerateGL generate all");
|
||||
Console.WriteLine(
|
||||
" StgSharp.GenerateGL generate src/StgSharp.Graphics/OpenGL/Generated");
|
||||
" StgSharp.GenerateGL generate all src/StgSharp.Graphics/OpenGL/Generated");
|
||||
Console.WriteLine(" StgSharp.GenerateGL list files");
|
||||
Console.WriteLine(" StgSharp.GenerateGL preview glconst.cs");
|
||||
Console.WriteLine(" StgSharp.GenerateGL xml tree --depth 1");
|
||||
|
||||
Reference in New Issue
Block a user