Files
stgsharp/test/StgSharp.GenerateGL/CodeGen/UniformOverloadEmitter.cs
T

220 lines
9.6 KiB
C#

//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="UniformOverloadEmitter"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
#pragma warning disable CA1305
namespace GenerateGL.CodeGen
{
internal static partial class UniformOverloadEmitter
{
public static bool IsValueCommand(
GlCommand command
)
{
return ValueCommandRegex().IsMatch(command.Name);
}
public static bool IsSetterCommand(
GlCommand command
)
{
return IsValueCommand(command) || MatrixCommandRegex().IsMatch(command.Name);
}
public static void Write(
string outputRoot,
string version,
IReadOnlyList<GlCommand> commands
)
{
List<GlCommand> valueCommands = commands.Where(IsValueCommand)
.OrderBy(c => c.Name, StringComparer.Ordinal)
.ToList();
List<GlCommand> matrixConvenienceCommands = commands
.Where(c => Matrix4FloatCommandRegex().IsMatch(c.Name))
.OrderBy(c => c.Name, StringComparer.Ordinal)
.ToList();
if (valueCommands.Count == 0 && matrixConvenienceCommands.Count == 0) {
return;
}
StringBuilder sb = new();
sb.AppendLine("// <auto-generated>");
sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}");
sb.AppendLine($"// Uniform overloads introduced in OpenGL {version}");
sb.AppendLine("// </auto-generated>");
sb.AppendLine("using System;");
sb.AppendLine("using System.Runtime.CompilerServices;");
sb.AppendLine("using StgSharp.Mathematics.Graphics;");
sb.AppendLine();
sb.AppendLine("namespace StgSharp.Graphics.OpenGL");
sb.AppendLine("{");
sb.AppendLine(" public unsafe partial class OpenGLFunction");
sb.AppendLine(" {");
foreach (GlCommand command in valueCommands) {
EmitValueOverload(sb, command);
}
foreach (GlCommand command in valueCommands.Where(IsFloatVectorCommand)) {
EmitVectorConvenienceOverload(sb, command);
}
foreach (GlCommand command in matrixConvenienceCommands) {
EmitMatrixConvenienceOverload(sb, command);
}
sb.AppendLine(" }");
sb.AppendLine("}");
string fileName = Path.Combine(
outputRoot,
$"OpenGLFunction.Uniform.v{version.Replace('.', '_')}.g.cs");
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private static void EmitValueOverload(
StringBuilder sb,
GlCommand command
)
{
Match match = ValueCommandRegex().Match(command.Name);
bool hasProgram = match.Groups[1].Success;
int dimension = int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
string scalarType = MapScalarType(match.Groups[3].Value);
bool isVector = match.Groups[4].Success;
string uniformType = $"Uniform<{string.Join(", ", Enumerable.Repeat(scalarType, dimension))}>";
sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
if (isVector)
{
string programParameter = hasProgram ? "GlHandle program, " : string.Empty;
sb.AppendLine($" public void SetUniformValue({programParameter}{uniformType} uniform, ReadOnlySpan<{scalarType}> values)");
sb.AppendLine(" {");
sb.AppendLine(" ArgumentNullException.ThrowIfNull(uniform);");
if (dimension > 1)
{
sb.AppendLine($" if ((values.Length % {dimension}) != 0) {{");
sb.AppendLine($" throw new ArgumentException(\"Uniform data length must be divisible by {dimension}.\", nameof(values));");
sb.AppendLine(" }");
}
sb.AppendLine($" fixed ({scalarType}* valuesPtr = values) {{");
string count = dimension == 1 ? "values.Length" : $"values.Length / {dimension}";
sb.AppendLine($" Context.{command.Name}({BuildPrefixArguments(hasProgram)}{count}, valuesPtr);");
sb.AppendLine(" }");
sb.AppendLine(" }");
} else
{
string programParameter = hasProgram ? "GlHandle program, " : string.Empty;
string values = string.Join(", ", Enumerable.Range(0, dimension)
.Select(i => $"{scalarType} v{i}"));
string valueArguments = string.Join(", ", Enumerable.Range(0, dimension)
.Select(i => $"v{i}"));
sb.AppendLine($" public void SetUniformValue({programParameter}{uniformType} uniform, {values})");
sb.AppendLine(" {");
sb.AppendLine(" ArgumentNullException.ThrowIfNull(uniform);");
sb.AppendLine($" Context.{command.Name}({BuildPrefixArguments(hasProgram)}{valueArguments});");
sb.AppendLine(" }");
}
sb.AppendLine();
}
private static void EmitVectorConvenienceOverload(
StringBuilder sb,
GlCommand command
)
{
Match match = ValueCommandRegex().Match(command.Name);
bool hasProgram = match.Groups[1].Success;
int dimension = int.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
if (dimension < 2) {
return;
}
string vectorType = $"Vec{dimension}";
string programParameter = hasProgram ? "GlHandle program, " : string.Empty;
sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public void SetUniformValue({programParameter}Uniform<{vectorType}> uniform, {vectorType} value)");
sb.AppendLine(" {");
sb.AppendLine(" ArgumentNullException.ThrowIfNull(uniform);");
sb.AppendLine($" Context.{command.Name}({BuildPrefixArguments(hasProgram)}1, (float*)&value);");
sb.AppendLine(" }");
sb.AppendLine();
}
private static void EmitMatrixConvenienceOverload(
StringBuilder sb,
GlCommand command
)
{
bool hasProgram = command.Name.StartsWith("glProgram", StringComparison.Ordinal);
string programParameter = hasProgram ? "GlHandle program, " : string.Empty;
sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
sb.AppendLine($" public void SetUniformValue({programParameter}Uniform<GraphicsMatrix> uniform, GraphicsMatrix value, bool transpose = false)");
sb.AppendLine(" {");
sb.AppendLine(" ArgumentNullException.ThrowIfNull(uniform);");
sb.AppendLine($" Context.{command.Name}({BuildPrefixArguments(hasProgram)}1, transpose ? (byte)1 : (byte)0, (float*)&value);");
sb.AppendLine(" }");
sb.AppendLine();
}
private static string BuildPrefixArguments(
bool hasProgram
)
{
return hasProgram
? "program.Value, uniform.id.SignedValue, "
: "uniform.id.SignedValue, ";
}
private static bool IsFloatVectorCommand(
GlCommand command
)
{
Match match = ValueCommandRegex().Match(command.Name);
return match.Success && match.Groups[3].Value == "f" && match.Groups[4].Success;
}
private static string MapScalarType(
string suffix
)
{
return suffix switch
{
"f" => "float",
"d" => "double",
"i" => "int",
"ui" => "uint",
_ => throw new InvalidOperationException($"Unsupported uniform suffix: {suffix}")
};
}
[GeneratedRegex("^gl(Program)?Uniform([1-4])(ui|[fdi])(v)?$",
RegexOptions.CultureInvariant)]
private static partial Regex ValueCommandRegex();
[GeneratedRegex("^gl(?:Program)?UniformMatrix4fv$", RegexOptions.CultureInvariant)]
private static partial Regex Matrix4FloatCommandRegex();
[GeneratedRegex("^gl(?:Program)?UniformMatrix[234](?:x[234])?[fd]v$",
RegexOptions.CultureInvariant)]
private static partial Regex MatrixCommandRegex();
}
}
#pragma warning restore CA1305