*
This commit is contained in:
@@ -0,0 +1,735 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
public sealed class Generator {
|
||||
private readonly VkSpec _spec;
|
||||
private readonly string _outDir;
|
||||
private readonly ReadOnlyDictionary<string, SugarClassSpec> _sugar;
|
||||
|
||||
public Generator(VkSpec spec, string outDir, IEnumerable<SugarClassSpec>? sugarSpecs = null) {
|
||||
_spec = spec;
|
||||
_outDir = outDir;
|
||||
_sugar = new ReadOnlyDictionary<string, SugarClassSpec>(
|
||||
(sugarSpecs ?? new List<SugarClassSpec>())
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s.TargetClass))
|
||||
.GroupBy(s => s.TargetClass)
|
||||
.ToDictionary(g => g.Key, g => {
|
||||
var merged = new SugarClassSpec(g.Key) {
|
||||
Includes = g.SelectMany(x => x.Includes).Distinct().ToList(),
|
||||
Methods = g.SelectMany(x => x.Methods).ToList()
|
||||
};
|
||||
return merged;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public void GenerateAll() {
|
||||
GenerateVulkanFunctions();
|
||||
GenerateHandleWrappers();
|
||||
GenerateProducerMemberMethods();
|
||||
GenerateMemberMethods();
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
static string ToClass(string vkType) => vkType.StartsWith("Vk") ? vkType.Substring(2) : vkType;
|
||||
|
||||
/// <summary>
|
||||
/// Build a pretty method name from a vk* command for a given class.
|
||||
/// Rules:
|
||||
/// - Strip leading 'vk'.
|
||||
/// - Strip the class token at BEGIN or END (Queue, Device, CommandBuffer...).
|
||||
/// - For CommandBuffer also strip "Cmd".
|
||||
/// - Remove first occurrence of class token inside the name if still present (e.g., PhysicalDevice).
|
||||
/// - LowerCamelCase.
|
||||
/// </summary>
|
||||
static string MakeMethodName(string className, string vkFunc) {
|
||||
string s = vkFunc.StartsWith("vk") ? vkFunc.Substring(2) : vkFunc;
|
||||
|
||||
var tokens = new List<string> { className };
|
||||
if (className == "CommandBuffer") tokens.Add("Cmd");
|
||||
|
||||
foreach (var t in tokens) {
|
||||
if (s.StartsWith(t, StringComparison.Ordinal)) { s = s.Substring(t.Length); break; }
|
||||
}
|
||||
foreach (var t in tokens) {
|
||||
if (s.EndsWith(t, StringComparison.Ordinal)) { s = s.Substring(0, s.Length - t.Length); break; }
|
||||
}
|
||||
int idx = s.IndexOf(className, StringComparison.Ordinal);
|
||||
if (idx >= 0) s = s.Remove(idx, className.Length);
|
||||
|
||||
if (s.Length == 0) return "";
|
||||
return char.ToLowerInvariant(s[0]) + s.Substring(1);
|
||||
}
|
||||
|
||||
static string Render(string tpl, Dictionary<string, string> vars) {
|
||||
foreach (var kv in vars) tpl = tpl.Replace("${" + kv.Key + "}", kv.Value);
|
||||
return tpl;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// VulkanFunctions — function pointers loader
|
||||
// =========================================
|
||||
void GenerateVulkanFunctions() {
|
||||
var sbH = new StringBuilder();
|
||||
sbH.AppendLine("#pragma once");
|
||||
sbH.AppendLine("#include <vulkan/vulkan.h>");
|
||||
sbH.AppendLine("#include <stdexcept>");
|
||||
sbH.AppendLine("#include <string>");
|
||||
sbH.AppendLine();
|
||||
sbH.AppendLine("struct VulkanFunctions {");
|
||||
|
||||
foreach (var cmd in _spec.Commands.Values.OrderBy(c => c.Name))
|
||||
sbH.AppendLine(" PFN_" + cmd.Name + " " + cmd.Name + " = nullptr;");
|
||||
|
||||
sbH.AppendLine();
|
||||
sbH.AppendLine(" void loadGlobal();");
|
||||
sbH.AppendLine(" void loadInstance(VkInstance instance);");
|
||||
sbH.AppendLine(" void loadDevice(VkDevice device);");
|
||||
sbH.AppendLine("};");
|
||||
sbH.AppendLine("extern VulkanFunctions vk;");
|
||||
File.WriteAllText(Path.Combine(_outDir, "VulkanFunctions.hpp"), sbH.ToString());
|
||||
|
||||
var sbCpp = new StringBuilder();
|
||||
sbCpp.AppendLine("#include \"VulkanFunctions.hpp\"");
|
||||
sbCpp.AppendLine("#if defined(_WIN32)");
|
||||
sbCpp.AppendLine("#include <windows.h>");
|
||||
sbCpp.AppendLine("static HMODULE g_vkLib = LoadLibraryA(\"vulkan-1.dll\");");
|
||||
sbCpp.AppendLine("static void* LoadSymbol(const char* n){ return (void*)GetProcAddress(g_vkLib, n); }");
|
||||
sbCpp.AppendLine("#else");
|
||||
sbCpp.AppendLine("#include <dlfcn.h>");
|
||||
sbCpp.AppendLine("static void* g_vkLib = dlopen(\"libvulkan.so.1\", RTLD_NOW | RTLD_LOCAL);");
|
||||
sbCpp.AppendLine("static void* LoadSymbol(const char* n){ return dlsym(g_vkLib, n); }");
|
||||
sbCpp.AppendLine("#endif");
|
||||
sbCpp.AppendLine("VulkanFunctions vk;");
|
||||
sbCpp.AppendLine();
|
||||
sbCpp.AppendLine("void VulkanFunctions::loadGlobal(){");
|
||||
sbCpp.AppendLine(" vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)LoadSymbol(\"vkGetInstanceProcAddr\");");
|
||||
foreach (var cmd in _spec.Commands.Values.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Global).OrderBy(c => c.Name))
|
||||
sbCpp.AppendLine(" " + cmd.Name + " = (PFN_" + cmd.Name + ")vkGetInstanceProcAddr(nullptr, \"" + cmd.Name + "\");");
|
||||
sbCpp.AppendLine("}");
|
||||
sbCpp.AppendLine();
|
||||
sbCpp.AppendLine("void VulkanFunctions::loadInstance(VkInstance instance){");
|
||||
foreach (var cmd in _spec.Commands.Values.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Instance).OrderBy(c => c.Name))
|
||||
sbCpp.AppendLine(" " + cmd.Name + " = (PFN_" + cmd.Name + ")vkGetInstanceProcAddr(instance, \"" + cmd.Name + "\");");
|
||||
sbCpp.AppendLine("}");
|
||||
sbCpp.AppendLine();
|
||||
sbCpp.AppendLine("void VulkanFunctions::loadDevice(VkDevice device){");
|
||||
sbCpp.AppendLine(" vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(nullptr, \"vkGetDeviceProcAddr\");");
|
||||
foreach (var cmd in _spec.Commands.Values.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Device).OrderBy(c => c.Name))
|
||||
sbCpp.AppendLine(" " + cmd.Name + " = (PFN_" + cmd.Name + ")vkGetDeviceProcAddr(device, \"" + cmd.Name + "\");");
|
||||
sbCpp.AppendLine("}");
|
||||
File.WriteAllText(Path.Combine(_outDir, "VulkanFunctions.cpp"), sbCpp.ToString());
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// RAII wrappers (owned + borrowed)
|
||||
// =========================================
|
||||
void GenerateHandleWrappers() {
|
||||
var handlesToGenerate = new HashSet<string>(_spec.Pairs.Select(p => p.HandleType));
|
||||
foreach (var pr in _spec.Producers) handlesToGenerate.Add(pr.HandleType);
|
||||
|
||||
foreach (var handleType in handlesToGenerate) {
|
||||
var className = ToClass(handleType);
|
||||
|
||||
VkSpec.Pair? pair = _spec.Pairs.FirstOrDefault(p => p.HandleType == handleType);
|
||||
var hasOwned = pair != null;
|
||||
var primaryOwners = new List<string>();
|
||||
|
||||
if (hasOwned) {
|
||||
primaryOwners.Add(pair!.ParentType);
|
||||
primaryOwners.AddRange(pair!.ExtraOwnersForDestroy.Where(o => o != pair!.ParentType));
|
||||
} else {
|
||||
var parents = _spec.Producers.Where(p => p.HandleType == handleType)
|
||||
.Select(p => p.ParentType)
|
||||
.Distinct().ToList();
|
||||
if (parents.Count > 0) primaryOwners.AddRange(parents);
|
||||
}
|
||||
primaryOwners = primaryOwners.Distinct().ToList();
|
||||
|
||||
var lifetimeUses = new List<VkSpec.OwnerUse>();
|
||||
if (hasOwned) {
|
||||
var createCmd = _spec.Commands[pair!.CreateOrAllocate];
|
||||
lifetimeUses = _spec.AnalyzeCreateInfoOwners(createCmd, handleType)
|
||||
.Where(u => !primaryOwners.Contains(u.Type))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var header = new StringBuilder();
|
||||
header.AppendLine("#pragma once");
|
||||
header.AppendLine("#include <vector>");
|
||||
header.AppendLine("#include <stdexcept>");
|
||||
header.AppendLine("#include <IntrusivePtr.h>");
|
||||
header.AppendLine("#include \"VulkanFunctions.hpp\"");
|
||||
header.AppendLine();
|
||||
|
||||
header.AppendLine("// === Lifetime summary (deterministic) ===");
|
||||
header.AppendLine("// Handle: " + handleType);
|
||||
var holdsStr = primaryOwners.Count > 0 ? string.Join(", ", primaryOwners.Select(ToClass)) : "—";
|
||||
header.AppendLine("// Holds (destroy/free owners + creation parent, or producer parent): " + holdsStr);
|
||||
var keepList = lifetimeUses.Select(u => ToClass(u.Type) + (u.IsMany ? "[]" : "") + " via " + u.SourceStruct + "::" + u.SourceMember);
|
||||
var keepsStr = keepList.Any() ? string.Join("; ", keepList) : "—";
|
||||
header.AppendLine("// Keeps (from *CreateInfo/*AllocateInfo): " + keepsStr);
|
||||
header.AppendLine("// Destructor calls vkDestroy*/vkFree* only if _owned == true.");
|
||||
header.AppendLine();
|
||||
|
||||
header.AppendLine("class " + className + " : public RefCounted {");
|
||||
header.AppendLine("public:");
|
||||
header.AppendLine(" " + handleType + " getHandle() const noexcept { return _handle; }");
|
||||
|
||||
var usedGetterNames = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (int i = 0; i < primaryOwners.Count; i++) {
|
||||
var oc = ToClass(primaryOwners[i]);
|
||||
var getter = "get" + oc; var unique = getter; int k = 2;
|
||||
while (!usedGetterNames.Add(unique)) unique = getter + k++;
|
||||
header.AppendLine(" IntrusivePtr<" + oc + "> " + unique + "() const noexcept { return _owner" + i + "; }");
|
||||
}
|
||||
foreach (var u in lifetimeUses) {
|
||||
var oc = ToClass(u.Type);
|
||||
var baseName = "get" + oc + (u.IsMany ? "s" : "");
|
||||
var unique = baseName; int k = 2;
|
||||
while (!usedGetterNames.Add(unique)) unique = baseName + k++;
|
||||
if (u.IsMany)
|
||||
header.AppendLine(" const std::vector<IntrusivePtr<" + oc + ">>& " + unique + "() const noexcept { return _lifetime_owners_" + oc + "; }");
|
||||
else
|
||||
header.AppendLine(" IntrusivePtr<" + oc + "> " + unique + "() const noexcept { return _lifetime_owner_" + oc + "; }");
|
||||
}
|
||||
|
||||
// adopt raw handle (borrowed)
|
||||
if (primaryOwners.Count > 0) {
|
||||
header.Append("public:\n static IntrusivePtr<" + className + "> fromHandle(");
|
||||
for (int i = 0; i < primaryOwners.Count; i++) {
|
||||
if (i > 0) header.Append(", ");
|
||||
header.Append("IntrusivePtr<" + ToClass(primaryOwners[i]) + "> owner" + i);
|
||||
}
|
||||
if (primaryOwners.Count > 0) header.Append(", ");
|
||||
header.Append(handleType + " handle");
|
||||
header.AppendLine(") {");
|
||||
header.Append(" return IntrusivePtr<" + className + ">(new " + className + "(");
|
||||
for (int i = 0; i < primaryOwners.Count; i++) {
|
||||
if (i > 0) header.Append(", ");
|
||||
header.Append("owner" + i);
|
||||
}
|
||||
if (primaryOwners.Count > 0) header.Append(", ");
|
||||
header.Append("handle, false");
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
header.Append(", ");
|
||||
header.Append(u.IsMany ? "std::vector<IntrusivePtr<" + cls + ">>()" : "IntrusivePtr<" + cls + ">()");
|
||||
}
|
||||
header.AppendLine("));");
|
||||
header.AppendLine(" }");
|
||||
}
|
||||
|
||||
// sugar methods
|
||||
if (_sugar.TryGetValue(className, out var sugarSpec)) {
|
||||
foreach (var m in sugarSpec.Methods) {
|
||||
if (!m.RequiresFunctions.All(fn => _spec.Commands.ContainsKey(fn))) continue;
|
||||
if (!m.RequiresExtensions.All(ext => _spec.Extensions.Contains(ext))) continue;
|
||||
|
||||
var (sig, prelude, vars, guard) = BuildSugarSignatureAndContext(m, primaryOwners, className);
|
||||
|
||||
if (!string.IsNullOrEmpty(guard)) header.AppendLine("#if " + guard);
|
||||
header.AppendLine(" " + sig + " {");
|
||||
if (!string.IsNullOrEmpty(prelude))
|
||||
foreach (var line in prelude.Split('\n'))
|
||||
if (line.Length > 0) header.AppendLine(" " + line);
|
||||
foreach (var ln in m.BodyLines) // FIX: only the current sugar method body
|
||||
header.AppendLine(" " + Render(ln, vars));
|
||||
header.AppendLine(" }");
|
||||
if (!string.IsNullOrEmpty(guard)) header.AppendLine("#endif");
|
||||
}
|
||||
}
|
||||
|
||||
// owned factories
|
||||
if (hasOwned) {
|
||||
var multiOut = pair!.OutHandleParam.LenAttr != null;
|
||||
var fac = BuildFactory(_spec.Commands[pair!.CreateOrAllocate], pair!, className, ToClass(pair!.ParentType), primaryOwners, lifetimeUses, multiOut);
|
||||
header.Append(fac.decl);
|
||||
header.Append(fac.body);
|
||||
header.AppendLine();
|
||||
}
|
||||
|
||||
// private ctor/dtor/fields
|
||||
header.AppendLine("private:");
|
||||
header.Append(" explicit " + className + "(");
|
||||
for (int i = 0; i < primaryOwners.Count; i++) {
|
||||
if (i > 0) header.Append(", ");
|
||||
header.Append("IntrusivePtr<" + ToClass(primaryOwners[i]) + "> owner" + i);
|
||||
}
|
||||
if (primaryOwners.Count > 0) header.Append(", ");
|
||||
header.Append(handleType + " handle, bool owned");
|
||||
foreach (var u in lifetimeUses) {
|
||||
header.Append(", ");
|
||||
var cls = ToClass(u.Type);
|
||||
header.Append(u.IsMany
|
||||
? "const std::vector<IntrusivePtr<" + cls + ">>& owners_" + cls
|
||||
: "IntrusivePtr<" + cls + "> owner_" + cls);
|
||||
}
|
||||
header.AppendLine(") : _handle(handle), _owned(owned)");
|
||||
for (int i = 0; i < primaryOwners.Count; i++)
|
||||
header.AppendLine(" , _owner" + i + "(std::move(owner" + i + "))");
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
header.AppendLine(u.IsMany
|
||||
? " , _lifetime_owners_" + cls + "(owners_" + cls + ")"
|
||||
: " , _lifetime_owner_" + cls + "(std::move(owner_" + cls + "))");
|
||||
}
|
||||
header.AppendLine(" {}");
|
||||
|
||||
header.AppendLine(" ~" + className + "() override {");
|
||||
header.AppendLine(" if (_handle && _owned) {");
|
||||
if (hasOwned) {
|
||||
var destroyCmd = _spec.Commands[pair!.DestroyOrFree];
|
||||
var args = new List<string>();
|
||||
int ownerIdx = 0;
|
||||
foreach (var p in destroyCmd.Params) {
|
||||
if (p.Type == handleType && p.IsPointer) { args.Add("1"); args.Add("&_handle"); continue; }
|
||||
if (p.Type == handleType && !p.IsPointer) { args.Add("_handle"); continue; }
|
||||
if (p.Name == "pAllocator") { args.Add("nullptr"); continue; }
|
||||
if (p.IsHandle) { args.Add("_owner" + (ownerIdx++) + "->getHandle()"); continue; }
|
||||
if (p.Name.EndsWith("Count")) args.Add("0"); else args.Add("0");
|
||||
}
|
||||
header.AppendLine(" (void)vk." + destroyCmd.Name + "(" + string.Join(", ", args) + ");");
|
||||
header.AppendLine(" _handle = VK_NULL_HANDLE;");
|
||||
}
|
||||
header.AppendLine(" }");
|
||||
header.AppendLine(" }");
|
||||
|
||||
header.AppendLine(" " + handleType + " _handle{VK_NULL_HANDLE};");
|
||||
header.AppendLine(" bool _owned{false};");
|
||||
for (int i = 0; i < primaryOwners.Count; i++)
|
||||
header.AppendLine(" IntrusivePtr<" + ToClass(primaryOwners[i]) + "> _owner" + i + ";");
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
header.AppendLine(u.IsMany
|
||||
? " std::vector<IntrusivePtr<" + cls + ">> _lifetime_owners_" + cls + ";"
|
||||
: " IntrusivePtr<" + cls + "> _lifetime_owner_" + cls + ";");
|
||||
}
|
||||
|
||||
header.AppendLine("};");
|
||||
|
||||
File.WriteAllText(Path.Combine(_outDir, className + ".hpp"), header.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Build sugar signature + prelude + placeholders + guard
|
||||
(string sig, string prelude, Dictionary<string, string> vars, string guard)
|
||||
BuildSugarSignatureAndContext(SugarMethodSpec m, List<string> primaryOwners, string className) {
|
||||
var vars = new Dictionary<string, string> {
|
||||
["class"] = className,
|
||||
["handle"] = "_handle",
|
||||
["vk"] = "vk",
|
||||
["selfOwners"] = string.Join(", ", Enumerable.Range(0, primaryOwners.Count).Select(i => "_owner" + i))
|
||||
};
|
||||
for (int i = 0; i < primaryOwners.Count; i++) {
|
||||
vars["owner" + i] = "_owner" + i;
|
||||
vars["owner" + i + ".handle"] = "_owner" + i + "->getHandle()";
|
||||
}
|
||||
|
||||
var prelude = new StringBuilder();
|
||||
var paramList = string.Join(", ", m.Parameters);
|
||||
|
||||
var rxScalar = new Regex(@"\bIntrusivePtr<\s*(\w+)\s*>\s+(\w+)\b");
|
||||
foreach (Match mt in rxScalar.Matches(paramList)) {
|
||||
var varName = mt.Groups[2].Value;
|
||||
vars[varName + ".handle"] = varName + "->getHandle()";
|
||||
}
|
||||
|
||||
var rxVector = new Regex(@"\bstd::vector\s*<\s*IntrusivePtr<\s*(\w+)\s*>\s*>\s*&?\s*(\w+)\b");
|
||||
foreach (Match mt in rxVector.Matches(paramList)) {
|
||||
var elemClass = mt.Groups[1].Value;
|
||||
var varName = mt.Groups[2].Value;
|
||||
var vkType = "Vk" + elemClass;
|
||||
|
||||
var rawName = "_raw_" + varName;
|
||||
prelude.AppendLine("std::vector<" + vkType + "> " + rawName + "; " + rawName + ".reserve(" + varName + ".size());");
|
||||
prelude.AppendLine("for (auto const& it : " + varName + ") " + rawName + ".push_back(it->getHandle());");
|
||||
|
||||
vars[varName + ".handles"] = rawName + ".data()";
|
||||
vars[varName + ".size"] = varName + ".size()";
|
||||
vars[varName + ".count"] = varName + ".size()";
|
||||
}
|
||||
|
||||
var quals = new StringBuilder();
|
||||
if (m.IsConst) quals.Append(" const");
|
||||
if (m.IsNoexcept) quals.Append(" noexcept");
|
||||
|
||||
var sig = (m.IsStatic ? "static " : "") + m.ReturnType + " " + m.Name + "(" + string.Join(", ", m.Parameters) + ")" + quals.ToString();
|
||||
|
||||
string guard = "";
|
||||
var parts = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(m.Guard)) parts.Add(m.Guard!);
|
||||
if (m.RequiresExtensions.Count > 0)
|
||||
parts.Add(string.Join(" && ", m.RequiresExtensions.Select(e => "defined(" + e + ")")));
|
||||
if (parts.Count > 0) guard = string.Join(" && ", parts);
|
||||
|
||||
return (sig, prelude.ToString(), vars, guard);
|
||||
}
|
||||
|
||||
// Owned factory: declaration + body (+ comment with vkCreate*)
|
||||
(string decl, string body) BuildFactory(
|
||||
VkSpec.Command create,
|
||||
VkSpec.Pair pair,
|
||||
string className,
|
||||
string parentClass,
|
||||
List<string> primaryOwners,
|
||||
List<VkSpec.OwnerUse> lifetimeUses,
|
||||
bool multiOut) {
|
||||
var sbDecl = new StringBuilder();
|
||||
var sbBody = new StringBuilder();
|
||||
|
||||
var ps = create.Params.Skip(1).ToList();
|
||||
var outLenName = pair.OutHandleParam.LenAttr;
|
||||
var lenNamesToSkip = new HashSet<string>();
|
||||
|
||||
var sigParams = new List<string>();
|
||||
var prep = new StringBuilder();
|
||||
var callArgs = new List<string>();
|
||||
|
||||
sbDecl.Append("public:\n ");
|
||||
// <-- Comment with original vk command
|
||||
sbDecl.Append("// Original: " + create.Name + "\n ");
|
||||
if (!multiOut)
|
||||
sbDecl.Append("static IntrusivePtr<" + className + "> create(IntrusivePtr<" + parentClass + "> parent");
|
||||
else
|
||||
sbDecl.Append("static std::vector<IntrusivePtr<" + className + ">> createMany(IntrusivePtr<" + parentClass + "> parent");
|
||||
|
||||
foreach (var p in ps) {
|
||||
if (p.Name == "pAllocator") continue;
|
||||
if (p.IsPointer && p.Type == pair.HandleType) continue;
|
||||
if (outLenName != null && p.Name == outLenName) continue;
|
||||
|
||||
if (p.IsHandle) {
|
||||
var cls = ToClass(p.Type);
|
||||
if (p.IsPointer && p.LenAttr != null) {
|
||||
sigParams.Add("const std::vector<IntrusivePtr<" + cls + ">>& " + p.Name);
|
||||
var rawVec = "_raw_" + p.Name;
|
||||
prep.AppendLine(" std::vector<" + p.Type + "> " + rawVec + "; " + rawVec + ".reserve(" + p.Name + ".size());");
|
||||
prep.AppendLine(" for (auto const& it : " + p.Name + ") " + rawVec + ".push_back(it->getHandle());");
|
||||
lenNamesToSkip.Add(p.LenAttr);
|
||||
callArgs.Add(p.Name + ".size()");
|
||||
callArgs.Add(rawVec + ".data()");
|
||||
} else {
|
||||
sigParams.Add("IntrusivePtr<" + cls + "> " + p.Name);
|
||||
callArgs.Add(p.Name + "->getHandle()");
|
||||
}
|
||||
} else {
|
||||
if (lenNamesToSkip.Contains(p.Name)) continue;
|
||||
sigParams.Add((p.IsConst ? "const " : "") + p.Type + (p.IsPointer ? "*" : "") + " " + p.Name);
|
||||
callArgs.Add(p.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var lifeSig = new List<string>();
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
lifeSig.Add(u.IsMany
|
||||
? "const std::vector<IntrusivePtr<" + cls + ">>& owners_" + cls
|
||||
: "IntrusivePtr<" + cls + "> owner_" + cls);
|
||||
}
|
||||
|
||||
if (sigParams.Count > 0) sbDecl.Append(", ");
|
||||
sbDecl.Append(string.Join(", ", sigParams));
|
||||
if (lifeSig.Count > 0) {
|
||||
if (sigParams.Count > 0) sbDecl.Append(", ");
|
||||
sbDecl.Append(string.Join(", ", lifeSig));
|
||||
}
|
||||
sbDecl.AppendLine(") {");
|
||||
|
||||
sbBody.Append(prep.ToString());
|
||||
|
||||
callArgs.Add("nullptr");
|
||||
|
||||
var outName = "_out";
|
||||
if (!multiOut) {
|
||||
sbBody.AppendLine(" " + pair.HandleType + " " + outName + " = VK_NULL_HANDLE;");
|
||||
callArgs.Add("&" + outName);
|
||||
} else {
|
||||
var lenName = outLenName ?? "count";
|
||||
sbBody.AppendLine(" std::vector<" + pair.HandleType + "> " + outName + ";");
|
||||
sbBody.AppendLine(" " + outName + ".resize(" + lenName + ");");
|
||||
callArgs.Add(outName + ".data()");
|
||||
}
|
||||
|
||||
var argsFinal = new List<string> { "parent->getHandle()" };
|
||||
argsFinal.AddRange(callArgs);
|
||||
sbBody.Append(" auto _res = vk." + create.Name + "(" + string.Join(", ", argsFinal) + ");\n");
|
||||
sbBody.AppendLine(" if (_res != VK_SUCCESS) throw std::runtime_error(\"" + create.Name + " failed\");");
|
||||
|
||||
if (!multiOut) {
|
||||
sbBody.Append(" return IntrusivePtr<" + className + ">(new " + className + "(");
|
||||
for (int i = 0; i < primaryOwners.Count; ++i) {
|
||||
if (i == 0) sbBody.Append("parent");
|
||||
else {
|
||||
var t = primaryOwners[i];
|
||||
var pFound = ps.FirstOrDefault(pp => pp.IsHandle && pp.Type == t);
|
||||
sbBody.Append(", " + (pFound != null ? (pFound.IsPointer && pFound.LenAttr != null ? "(" + ToClass(t) + ")nullptr /*from array*/" : pFound.Name) : "parent"));
|
||||
}
|
||||
}
|
||||
if (primaryOwners.Count > 0) sbBody.Append(", ");
|
||||
sbBody.Append(outName);
|
||||
sbBody.Append(", true");
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
sbBody.Append(", ");
|
||||
sbBody.Append(u.IsMany ? "owners_" + cls : "owner_" + cls);
|
||||
}
|
||||
sbBody.AppendLine("));");
|
||||
} else {
|
||||
sbBody.AppendLine(" std::vector<IntrusivePtr<" + className + ">> _ret; _ret.reserve(" + outName + ".size());");
|
||||
sbBody.AppendLine(" for (auto h : " + outName + ") {");
|
||||
sbBody.Append(" _ret.emplace_back(new " + className + "(");
|
||||
for (int i = 0; i < primaryOwners.Count; ++i) {
|
||||
if (i == 0) sbBody.Append("parent");
|
||||
else {
|
||||
var t = primaryOwners[i];
|
||||
var pFound = ps.FirstOrDefault(pp => pp.IsHandle && pp.Type == t);
|
||||
sbBody.Append(", " + (pFound != null ? (pFound.IsPointer && pFound.LenAttr != null ? "(" + ToClass(t) + ")nullptr /*from array*/" : pFound.Name) : "parent"));
|
||||
}
|
||||
}
|
||||
if (primaryOwners.Count > 0) sbBody.Append(", ");
|
||||
sbBody.Append("h");
|
||||
sbBody.Append(", true");
|
||||
foreach (var u in lifetimeUses) {
|
||||
var cls = ToClass(u.Type);
|
||||
sbBody.Append(", ");
|
||||
sbBody.Append(u.IsMany ? "owners_" + cls : "owner_" + cls);
|
||||
}
|
||||
sbBody.AppendLine("));");
|
||||
sbBody.AppendLine(" }");
|
||||
sbBody.AppendLine(" return _ret;");
|
||||
}
|
||||
|
||||
sbBody.AppendLine(" }");
|
||||
|
||||
return (sbDecl.ToString(), sbBody.ToString());
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Producer methods (Get*/Enumerate*) → parent->getChild()
|
||||
// =========================================
|
||||
void GenerateProducerMemberMethods() {
|
||||
var headers = Directory.GetFiles(_outDir, "*.hpp")
|
||||
.ToDictionary(f => Path.GetFileNameWithoutExtension(f), f => new StringBuilder(File.ReadAllText(f)));
|
||||
|
||||
foreach (var pr in _spec.Producers) {
|
||||
var parentClass = ToClass(pr.ParentType);
|
||||
var childClass = ToClass(pr.HandleType);
|
||||
if (!headers.TryGetValue(parentClass, out var h)) {
|
||||
var minimal = new StringBuilder();
|
||||
minimal.AppendLine("#pragma once");
|
||||
minimal.AppendLine("#include <vector>");
|
||||
minimal.AppendLine("#include <IntrusivePtr.h>");
|
||||
minimal.AppendLine("#include \"VulkanFunctions.hpp\"");
|
||||
minimal.AppendLine("class " + parentClass + " : public RefCounted {");
|
||||
minimal.AppendLine("public:");
|
||||
minimal.AppendLine(" " + pr.ParentType + " getHandle() const noexcept { return _handle; }");
|
||||
minimal.AppendLine("private:");
|
||||
minimal.AppendLine(" " + pr.ParentType + " _handle{VK_NULL_HANDLE};");
|
||||
minimal.AppendLine("};");
|
||||
h = minimal;
|
||||
headers[parentClass] = h;
|
||||
}
|
||||
|
||||
var cmd = _spec.Commands[pr.CommandName];
|
||||
|
||||
var outParam = pr.OutHandleParam;
|
||||
var lenName = outParam.LenAttr;
|
||||
var sigParams = new List<string>();
|
||||
var callArgs = new List<string>();
|
||||
foreach (var p in cmd.Params) {
|
||||
if (p == cmd.Params[0]) continue;
|
||||
if (p.Name == outParam.Name) continue;
|
||||
if (!string.IsNullOrEmpty(lenName) && p.Name == lenName) continue;
|
||||
|
||||
if (p.IsHandle) {
|
||||
var cls = ToClass(p.Type);
|
||||
sigParams.Add("IntrusivePtr<" + cls + "> " + p.Name);
|
||||
callArgs.Add(p.Name + "->getHandle()");
|
||||
} else {
|
||||
sigParams.Add((p.IsConst ? "const " : "") + p.Type + (p.IsPointer ? "*" : "") + " " + p.Name);
|
||||
callArgs.Add(p.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var retWrapper = pr.IsMany
|
||||
? "std::vector<IntrusivePtr<" + childClass + ">>"
|
||||
: "IntrusivePtr<" + childClass + ">";
|
||||
|
||||
var methodName = "get" + childClass + (pr.IsMany ? "s" : "");
|
||||
var method = new StringBuilder();
|
||||
method.Append("public:\n ");
|
||||
// <-- Comment with original vkGet*/vkEnumerate*
|
||||
method.Append("// Original: " + cmd.Name + "\n ");
|
||||
method.Append(retWrapper + " " + methodName + "(" + string.Join(", ", sigParams) + ") {\n");
|
||||
if (pr.IsMany) {
|
||||
var countParam = cmd.Params.FirstOrDefault(p => p.Name == lenName)!;
|
||||
var countType = countParam.Type;
|
||||
method.Append(" " + countType + " _count = 0;\n");
|
||||
|
||||
var firstArgs = new List<string> { "_handle" };
|
||||
foreach (var p in cmd.Params.Skip(1)) {
|
||||
if (p.Name == lenName) firstArgs.Add("&_count");
|
||||
else if (p.Name == outParam.Name) firstArgs.Add("nullptr");
|
||||
else if (p.IsHandle) firstArgs.Add(p.Name + "->getHandle()");
|
||||
else firstArgs.Add(p.Name);
|
||||
}
|
||||
var call1 = "vk." + cmd.Name + "(" + string.Join(", ", firstArgs) + ")";
|
||||
if (cmd.ReturnType.Trim() == "VkResult") {
|
||||
method.Append(" auto _r1 = " + call1 + ";\n");
|
||||
method.Append(" if (_r1 != VK_SUCCESS && _r1 != VK_INCOMPLETE) throw std::runtime_error(\"" + cmd.Name + " (count) failed\");\n");
|
||||
} else {
|
||||
method.Append(" " + call1 + ";\n");
|
||||
}
|
||||
|
||||
method.Append(" std::vector<" + pr.HandleType + "> _out;\n");
|
||||
method.Append(" _out.resize(_count);\n");
|
||||
|
||||
var secondArgs = new List<string> { "_handle" };
|
||||
foreach (var p in cmd.Params.Skip(1)) {
|
||||
if (p.Name == lenName) secondArgs.Add("&_count");
|
||||
else if (p.Name == outParam.Name) secondArgs.Add("_out.data()");
|
||||
else if (p.IsHandle) secondArgs.Add(p.Name + "->getHandle()");
|
||||
else secondArgs.Add(p.Name);
|
||||
}
|
||||
var call2 = "vk." + cmd.Name + "(" + string.Join(", ", secondArgs) + ")";
|
||||
if (cmd.ReturnType.Trim() == "VkResult") {
|
||||
method.Append(" auto _r2 = " + call2 + ";\n");
|
||||
method.Append(" if (_r2 != VK_SUCCESS) throw std::runtime_error(\"" + cmd.Name + " (fill) failed\");\n");
|
||||
} else {
|
||||
method.Append(" " + call2 + ";\n");
|
||||
}
|
||||
|
||||
method.Append(" std::vector<IntrusivePtr<" + childClass + ">> _ret; _ret.reserve(_out.size());\n");
|
||||
method.Append(" IntrusivePtr<" + parentClass + "> _self(this);\n");
|
||||
method.Append(" for (auto h : _out) {\n");
|
||||
method.Append(" _ret.emplace_back(new " + childClass + "(_self, h, false));\n");
|
||||
method.Append(" }\n");
|
||||
method.Append(" return _ret;\n");
|
||||
} else {
|
||||
method.Append(" " + pr.HandleType + " _h = VK_NULL_HANDLE;\n");
|
||||
var args = new List<string> { "_handle" };
|
||||
foreach (var p in cmd.Params.Skip(1)) {
|
||||
if (p.Name == outParam.Name) args.Add("&_h");
|
||||
else if (p.IsHandle) args.Add(p.Name + "->getHandle()");
|
||||
else args.Add(p.Name);
|
||||
}
|
||||
var call = "vk." + cmd.Name + "(" + string.Join(", ", args) + ")";
|
||||
if (cmd.ReturnType.Trim() == "VkResult") {
|
||||
method.Append(" auto _r = " + call + ";\n");
|
||||
method.Append(" if (_r != VK_SUCCESS) throw std::runtime_error(\"" + cmd.Name + " failed\");\n");
|
||||
} else {
|
||||
method.Append(" " + call + ";\n");
|
||||
}
|
||||
method.Append(" return IntrusivePtr<" + childClass + ">(new " + childClass + "(IntrusivePtr<" + parentClass + ">(this), _h, false));\n");
|
||||
}
|
||||
method.Append(" }\n");
|
||||
|
||||
var s = h.ToString();
|
||||
var idx = s.LastIndexOf("};", StringComparison.Ordinal);
|
||||
s = s.Insert(idx, method.ToString());
|
||||
headers[parentClass] = new StringBuilder(s);
|
||||
}
|
||||
|
||||
foreach (var kv in headers)
|
||||
File.WriteAllText(Path.Combine(_outDir, kv.Key + ".hpp"), kv.Value.ToString());
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// vk* → class methods (skip producer-out-handle) with pretty names
|
||||
// =========================================
|
||||
void GenerateMemberMethods() {
|
||||
var headers = Directory.GetFiles(_outDir, "*.hpp")
|
||||
.ToDictionary(f => Path.GetFileNameWithoutExtension(f), f => new StringBuilder(File.ReadAllText(f)));
|
||||
|
||||
foreach (var cmd in _spec.Commands.Values) {
|
||||
if (cmd.Name.StartsWith("vkCreate") || cmd.Name.StartsWith("vkDestroy") ||
|
||||
cmd.Name.StartsWith("vkAllocate") || cmd.Name.StartsWith("vkFree"))
|
||||
continue;
|
||||
|
||||
bool isProducerOutHandle =
|
||||
(cmd.Name.StartsWith("vkGet") || cmd.Name.StartsWith("vkEnumerate")) &&
|
||||
cmd.Params.Any(p => p.IsPointer && _spec.Handles.ContainsKey(p.Type));
|
||||
if (isProducerOutHandle) continue;
|
||||
|
||||
if (cmd.Params.Count == 0) continue;
|
||||
var first = cmd.Params[0];
|
||||
if (!_spec.Handles.ContainsKey(first.Type)) continue;
|
||||
|
||||
var className = ToClass(first.Type);
|
||||
if (!headers.TryGetValue(className, out var h)) {
|
||||
var minimal = new StringBuilder();
|
||||
minimal.AppendLine("#pragma once");
|
||||
minimal.AppendLine("#include <vector>");
|
||||
minimal.AppendLine("#include <IntrusivePtr.h>");
|
||||
minimal.AppendLine("#include \"VulkanFunctions.hpp\"");
|
||||
minimal.AppendLine("class " + className + " : public RefCounted {");
|
||||
minimal.AppendLine("public:");
|
||||
minimal.AppendLine(" " + first.Type + " getHandle() const noexcept { return _handle; }");
|
||||
minimal.AppendLine("private:");
|
||||
minimal.AppendLine(" " + first.Type + " _handle{VK_NULL_HANDLE};");
|
||||
minimal.AppendLine("};");
|
||||
h = minimal;
|
||||
headers[className] = h;
|
||||
}
|
||||
|
||||
var methodName = MakeMethodName(className, cmd.Name);
|
||||
var retType = string.IsNullOrWhiteSpace(cmd.ReturnType) ? "void" : cmd.ReturnType;
|
||||
|
||||
var ps = cmd.Params.Skip(1).ToList();
|
||||
var sigParams = new List<string>();
|
||||
var prep = new StringBuilder();
|
||||
var callArgs = new List<string>();
|
||||
var lenNamesToSkip = new HashSet<string>();
|
||||
|
||||
foreach (var p in ps) {
|
||||
if (p.IsHandle) {
|
||||
var cls = ToClass(p.Type);
|
||||
if (p.IsPointer && p.LenAttr != null) {
|
||||
sigParams.Add("const std::vector<IntrusivePtr<" + cls + ">>& " + p.Name);
|
||||
var raw = "_raw_" + p.Name;
|
||||
prep.AppendLine(" std::vector<" + p.Type + "> " + raw + "; " + raw + ".reserve(" + p.Name + ".size());");
|
||||
prep.AppendLine(" for (auto const& it : " + p.Name + ") " + raw + ".push_back(it->getHandle());");
|
||||
lenNamesToSkip.Add(p.LenAttr);
|
||||
callArgs.Add(p.Name + ".size()");
|
||||
callArgs.Add(raw + ".data()");
|
||||
} else {
|
||||
sigParams.Add("IntrusivePtr<" + cls + "> " + p.Name);
|
||||
callArgs.Add(p.Name + "->getHandle()");
|
||||
}
|
||||
} else {
|
||||
if (lenNamesToSkip.Contains(p.Name)) continue;
|
||||
sigParams.Add((p.IsConst ? "const " : "") + p.Type + (p.IsPointer ? "*" : "") + " " + p.Name);
|
||||
callArgs.Add(p.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var method = new StringBuilder();
|
||||
method.Append("public:\n ");
|
||||
// <-- Comment with original vk* name
|
||||
method.Append("// Original: " + cmd.Name + "\n ");
|
||||
method.Append(retType + " " + methodName + "(" + string.Join(", ", sigParams) + ") {\n");
|
||||
method.Append(prep.ToString());
|
||||
method.Append(" ");
|
||||
if (retType != "void") method.Append("return ");
|
||||
var call = "vk." + cmd.Name + "(_handle";
|
||||
if (callArgs.Count > 0) call += ", " + string.Join(", ", callArgs);
|
||||
call += ");";
|
||||
method.Append(call + "\n");
|
||||
method.Append(" }\n");
|
||||
|
||||
var s = h.ToString();
|
||||
var idx = s.LastIndexOf("};", StringComparison.Ordinal);
|
||||
s = s.Insert(idx, method.ToString());
|
||||
headers[className] = new StringBuilder(s);
|
||||
}
|
||||
|
||||
foreach (var kv in headers)
|
||||
File.WriteAllText(Path.Combine(_outDir, kv.Key + ".hpp"), kv.Value.ToString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user