//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // 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 commands ) { List valueCommands = commands.Where(IsValueCommand) .OrderBy(c => c.Name, StringComparer.Ordinal) .ToList(); List 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("// "); sb.AppendLine($"// Generated by StgSharp.GenerateGL on {DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture)}"); sb.AppendLine($"// Uniform overloads introduced in OpenGL {version}"); sb.AppendLine("// "); 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 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