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