// Generate handle types private static void GenerateHandles(Vendor vendor) { if (vendor.Handles.Count == 0) { return; } // Make path and file context var vpath = vendor.IsCore ? "Handles.cs" : Path.Combine(vendor.Tag, "Handles.cs"); using var file = new SourceFile(vpath); // For each handle foreach (var handle in vendor.Handles.Values) { // Open block using var block = file.PushBlock($"public unsafe sealed partial class {handle.Name} : IVulkanHandle<{handle.Name}>"); // Get parent info var tableType = (handle.FunctionParent == "VkInstance") ? "InstanceFunctionTable" : "DeviceFunctionTable"; // Write fields block.WriteLine($"public readonly VulkanHandle<{handle.Name}> Handle;"); if (handle.Parent is not null) { block.WriteLine($"public readonly {handle.Parent.Name} Parent;"); } block.WriteLine($"public readonly {tableType} Functions;"); block.WriteLine("public bool IsValid => Handle.IsValid;"); block.WriteLine(); // Write ctor if (handle.Name == "VkInstance") { using var ctor = block.PushBlock("public VkInstance(VulkanHandle<VkInstance> handle, VkVersion apiVersion)"); ctor.WriteLine("Handle = handle;"); ctor.WriteLine("Functions = handle ? new(handle, apiVersion) : new();"); } else if (handle.Name == "VkDevice") { using var ctor = block.PushBlock("public VkDevice(VulkanHandle<VkDevice> handle, VkPhysicalDevice parent, VkVersion apiVersion)"); ctor.WriteLine("Handle = handle;"); ctor.WriteLine("Parent = parent;"); ctor.WriteLine("Functions = handle ? new(handle, apiVersion) : new();"); } else { using var ctor = block.PushBlock($"public {handle.Name}(VulkanHandle<{handle.Name}> handle, {handle.Parent!.Name} parent)"); ctor.WriteLine("Handle = handle;"); ctor.WriteLine("Parent = parent;"); ctor.WriteLine("Functions = parent.Functions;"); } // Write overrides block.WriteLine($"public override int GetHashCode() => Handle.GetHashCode();"); block.WriteLine($"public override string? ToString() => Handle.ToString();"); block.WriteLine($"public override bool Equals(object? o) => (o is {handle.Name} h) && (h.Handle == Handle);"); block.WriteLine($"bool IEquatable<{handle.Name}>.Equals({handle.Name}? other) => other?.Handle == Handle;"); block.WriteLine(); // Equality block.WriteLine($"public static bool operator == ({handle.Name}? l, {handle.Name}? r) => l?.Handle == r?.Handle;"); block.WriteLine($"public static bool operator != ({handle.Name}? l, {handle.Name}? r) => l?.Handle != r?.Handle;"); block.WriteLine(); // Casting block.WriteLine($"public static implicit operator VulkanHandle<{handle.Name}> ({handle.Name}? h) => h?.Handle ?? VulkanHandle<{handle.Name}>.Null;"); block.WriteLine($"public static implicit operator bool ({handle.Name}? h) => h?.IsValid ?? false;"); block.WriteLine(); // Handle functions foreach (var cmd in handle.Commands) { var hasRet = cmd.ReturnType != "void"; var retStr = hasRet ? "return " : String.Empty; foreach (var pset in cmd.ParamSets) { // If this is a (parent, handle, ...) function var parentArg = (pset.Count > 1) && (pset[1].Type == $"VulkanHandle<{handle.Name}>"); var anyFix = pset.Any(par => par.NeedsFix); var fnname = cmd.Name.Substring("vk".Length); block.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); if (cmd.Scope == CommandType.CommandScope.Global) { var protostr = String.Join(", ", pset.Select(par => $"{par.Type} {par.Name}")); var callstr = String.Join(", ", pset.Select(par => par.NeedsFix ? (par.Name + "FIXED") : par.Name)); if (anyFix) { using var func = block.PushBlock($"public static {cmd.ReturnType} {fnname}({protostr})"); foreach (var par in pset.Where(par => par.NeedsFix)) { func.WriteLine($"fixed ({par.Type.Substring(par.Type.IndexOf(' ') + 1)}* {par.Name}FIXED = &{par.Name})"); } func.WriteLine($"{retStr}InstanceFunctionTable.{cmd.Name}({callstr});"); } else { block.WriteLine($"public static {cmd.ReturnType} {fnname}({protostr})"); block.WriteLine($"\t=> InstanceFunctionTable.{cmd.Name}({callstr});"); block.WriteLine(); } } else { var protostr = String.Join(", ", pset.Skip(parentArg ? 2 : 1).Select(par => $"{par.Type} {par.Name}")); var callstr = String.Join(", ", pset.Skip(parentArg ? 2 : 1).Select(par => par.NeedsFix ? (par.Name + "FIXED") : par.Name)); if (anyFix) { using var func = block.PushBlock($"public {cmd.ReturnType} {fnname}({protostr})"); foreach (var par in pset.Where(par => par.NeedsFix)) { func.WriteLine($"fixed ({par.Type.Substring(par.Type.IndexOf(' ') + 1)}* {par.Name}FIXED = &{par.Name})"); } if (parentArg) { func.WriteLine($"{retStr}Functions.{cmd.Name}(Parent, Handle{(callstr.Length > 0 ? ", " : "")}{callstr});"); } else { func.WriteLine($"{retStr}Functions.{cmd.Name}(Handle{(callstr.Length > 0 ? ", " : "")}{callstr});"); } } else { block.WriteLine($"public {cmd.ReturnType} {fnname}({protostr})"); if (parentArg) { block.WriteLine($"\t=> Functions.{cmd.Name}(Parent, Handle{(callstr.Length > 0 ? ", " : "")}{callstr});"); } else { block.WriteLine($"\t=> Functions.{cmd.Name}(Handle{(callstr.Length > 0 ? ", " : "")}{callstr});"); } block.WriteLine(); } } } } } }
public SourceBlock(SourceFile file) { File = file; Depth = file.BlockDepth; }
// Generate struct types private static void GenerateStructs(Vendor vendor) { if (vendor.Structs.Count == 0) { return; } // Make path and file context var vpath = vendor.IsCore ? "Structs.cs" : Path.Combine(vendor.Tag, "Structs.cs"); using var file = new SourceFile(vpath); // For each struct List <string> ctorArgs = new(); List <string> ctorAssigns = new(); List <string> hashes = new(); List <string> comparisons = new(); foreach (var @struct in vendor.Structs.Values) { // Open block file.WriteLine($"[StructLayout(LayoutKind.{(@struct.IsUnion ? "Explicit" : "Sequential")})]"); using var block = file.PushBlock($"public unsafe partial struct {@struct.Name} : IEquatable<{@struct.Name}>"); // Typed structs if (@struct.IsTyped) { block.WriteLine($"public const VkStructureType TYPE = VkStructureType.{@struct.Type!};"); block.WriteLine(); } // Fields (and ctor arguments) ctorArgs.Clear(); ctorAssigns.Clear(); hashes.Clear(); comparisons.Clear(); foreach (var field in @struct.Fields) { var lower = NameHelper.GetSafeArgName(field.Name); if (field.ArraySize is not null) { if (field.IsFixed) { if (@struct.IsUnion) { block.WriteLine("[FieldOffset(0)]"); } block.WriteLine($"public fixed {field.Type} {field.Name}[{field.ArraySize}];"); for (uint i = 0; i < field.ArraySize; ++i) { ctorArgs.Add($"{field.Type} {lower}_{i} = default"); ctorAssigns.Add($"{field.Name}[{i}] = {lower}_{i};"); hashes.Add($"{field.Name}[{i}].GetHashCode()"); comparisons.Add($"(l.{field.Name}[{i}] == r.{field.Name}[{i}])"); } } else { for (uint i = 0; i < field.ArraySize; ++i) { block.WriteLine($"public {field.Type} {field.Name}_{i};"); ctorArgs.Add($"{field.Type} {lower}_{i} = default"); ctorAssigns.Add($"{field.Name}_{i} = {lower}_{i};"); hashes.Add($"{field.Name}_{i}.GetHashCode()"); comparisons.Add($"(l.{field.Name}_{i} == r.{field.Name}_{i})"); } } } else { if (@struct.IsUnion) { block.WriteLine("[FieldOffset(0)]"); } block.WriteLine($"public {field.Type} {field.Name};"); if (field.Name == "sType") { ctorAssigns.Add("sType = TYPE;"); } else if (field.Name == "pNext") { ctorAssigns.Add("pNext = null;"); } else { ctorArgs.Add($"{field.Type} {lower} = default"); ctorAssigns.Add($"{field.Name} = {lower};"); } var isPtr = field.Type.Contains('*'); hashes.Add(isPtr ? $"((ulong){field.Name}).GetHashCode()" : $"{field.Name}.GetHashCode()"); comparisons.Add($"(l.{field.Name} == r.{field.Name})"); } } block.WriteLine(); // Ctor if ([email protected] && ([email protected] || (@struct.Fields.Count > 2))) { block.WriteLine($"public {@struct.Name}("); for (int i = 0; i < ctorArgs.Count; ++i) { block.WriteLine($"\t{ctorArgs[i]}{((i == ctorArgs.Count - 1) ? "" : ",")}"); } block.WriteLine(") {"); foreach (var line in ctorAssigns) { block.WriteLine('\t' + line); } block.WriteLine("}"); block.WriteLine(); } else if (@struct.IsUnion) { foreach (var field in @struct.Fields) { var fname = NameHelper.GetSafeArgName(field.Name); if (field.ArraySize is not null) { block.WriteLine($"public {@struct.Name}("); for (uint i = 0; i < field.ArraySize; ++i) { block.WriteLine($"\tin {field.Type} {fname}_{i}{((i != field.ArraySize - 1) ? ", " : "")}"); } block.WriteLine(") {"); for (uint i = 0; i < field.ArraySize; ++i) { block.WriteLine('\t' + (field.IsFixed ? $"{field.Name}[{i}]" : $"{field.Name}_{i}") + $" = {fname}_{i};"); } block.WriteLine("}"); } else { block.WriteLine($"public {@struct.Name}(in {field.Type} {fname}) : this() => {field.Name} = {fname};"); } block.WriteLine(); } } // Equality block.WriteLine($"public readonly override bool Equals(object? o) => (o is {@struct.Name} s) && (this == s);"); block.WriteLine($"readonly bool IEquatable<{@struct.Name}>.Equals({@struct.Name} o) => o == this;"); block.WriteLine(); // Hash code block.WriteLine("[MethodImpl(MethodImplOptions.AggressiveOptimization)]"); using (var hash = block.PushBlock("public readonly override int GetHashCode()")) { hash.WriteLine("return"); for (int i = 0; i < hashes.Count; i += 4) { hash.WriteLine('\t' + (i != 0 ? "^ " : "") + String.Join(" ^ ", hashes.Skip(i).Take(4))); } hash.WriteLine("\t;"); } // Equality operators block.WriteLine("[MethodImpl(MethodImplOptions.AggressiveOptimization)]"); using (var op = block.PushBlock($"public static bool operator == (in {@struct.Name} l, in {@struct.Name} r)")) { op.WriteLine("return"); for (int i = 0; i < comparisons.Count; i += 4) { op.WriteLine('\t' + (i != 0 ? "&& " : "") + String.Join(" && ", comparisons.Skip(i).Take(4))); } op.WriteLine("\t;"); } block.WriteLine("[MethodImpl(MethodImplOptions.AggressiveOptimization)]"); using (var op = block.PushBlock($"public static bool operator != (in {@struct.Name} l, in {@struct.Name} r)")) { op.WriteLine("return"); for (int i = 0; i < comparisons.Count; i += 4) { op.WriteLine('\t' + (i != 0 ? "|| " : "") + String.Join(" || ", comparisons.Skip(i).Take(4).Select(cmp => cmp.Replace("==", "!=")))); } op.WriteLine("\t;"); } if ([email protected]) { // New function block.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); if (@struct.IsTyped) { block.WriteLine($"public static void New(out {@struct.Name} s) => s = new() {{ sType = TYPE }};"); } else { block.WriteLine($"public static void New(out {@struct.Name} s) => s = new();"); } } } }