1012 lines
51 KiB
C#
1012 lines
51 KiB
C#
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;
|
|
|
|
// Per-class header/source builders
|
|
private readonly Dictionary<string, StringBuilder> _headers = new(StringComparer.Ordinal);
|
|
private readonly Dictionary<string, StringBuilder> _sources = new(StringComparer.Ordinal);
|
|
|
|
// Forward declarations for headers, and includes needed in .cpp
|
|
private readonly Dictionary<string, HashSet<string>> _headerForwards = new(StringComparer.Ordinal);
|
|
private readonly Dictionary<string, HashSet<string>> _cppIncludes = new(StringComparer.Ordinal);
|
|
|
|
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();
|
|
GenerateHandleWrappersSkeletons(); // class skeletons (.hpp) + ctor/dtor declarations
|
|
GenerateFactories(); // ctor / createMany: prototypes (inside class) + .cpp bodies
|
|
GenerateProducerMemberMethods(); // get*/enumerate*: prototypes (inside class) + .cpp bodies
|
|
GenerateMemberMethods(); // other methods: prototypes (inside class) + .cpp bodies
|
|
GenerateSugarMethods(); // sugar: prototypes (inside class) + .cpp bodies
|
|
FinalizeHeadersAndSources(); // insert forwards; write .hpp/.cpp files
|
|
}
|
|
|
|
// ---------- helpers ----------
|
|
IEnumerable<VkSpec.Command> EnabledCommands()
|
|
=> _spec.Commands.Values.Where(c => _spec.ReachableCommands.Contains(c.Name));
|
|
|
|
static string ToClass(string vkType) => vkType.StartsWith("Vk") ? vkType.Substring(2) : vkType;
|
|
|
|
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 GuardExpr(HashSet<string> macros) {
|
|
if (macros == null || macros.Count == 0) return "";
|
|
return string.Join(" || ", macros.Select(m => "defined(" + m + ")"));
|
|
}
|
|
|
|
static string InsertIntoClassText(string text, string className, string snippet) {
|
|
// find "class <name>" then the opening '{'
|
|
int cls = text.IndexOf("class " + className, StringComparison.Ordinal);
|
|
if (cls < 0) return text;
|
|
int open = text.IndexOf('{', cls);
|
|
if (open < 0) return text;
|
|
|
|
// walk and find the matching closing '}' for that class body
|
|
int depth = 1;
|
|
int i = open + 1;
|
|
for (; i < text.Length; i++) {
|
|
char c = text[i];
|
|
if (c == '{') depth++;
|
|
else if (c == '}') {
|
|
depth--;
|
|
if (depth == 0) {
|
|
// insert BEFORE this '}' (i points at the closing brace)
|
|
var ins = snippet;
|
|
if (!ins.StartsWith("\n")) ins = "\n" + ins; // keep formatting clean
|
|
return text.Insert(i, ins);
|
|
}
|
|
}
|
|
}
|
|
// fallback: nothing inserted
|
|
return text;
|
|
}
|
|
|
|
|
|
void AddToHeaderClass(string className, string snippet) {
|
|
if (!_headers.TryGetValue(className, out var h)) return;
|
|
var s = h.ToString();
|
|
s = InsertIntoClassText(s, className, snippet);
|
|
_headers[className] = new StringBuilder(s);
|
|
}
|
|
|
|
void TouchHeader(string className) {
|
|
if (_headers.ContainsKey(className)) return;
|
|
|
|
var h = new StringBuilder();
|
|
h.AppendLine("#pragma once");
|
|
h.AppendLine("#include <vector>");
|
|
h.AppendLine("#include <stdexcept>");
|
|
h.AppendLine("#include <IntrusivePtr.h>");
|
|
h.AppendLine("#include <VulkanPP/VulkanFunctions.hpp>");
|
|
h.AppendLine();
|
|
h.AppendLine("namespace VulkanPP {");
|
|
h.AppendLine();
|
|
// forward declarations inserted later
|
|
_headers[className] = h;
|
|
|
|
// Init .cpp with own include; other includes inserted later
|
|
var s = new StringBuilder();
|
|
s.AppendLine("#include <VulkanPP/" + className + ".hpp>");
|
|
s.AppendLine();
|
|
s.AppendLine("namespace VulkanPP {");
|
|
s.AppendLine();
|
|
_sources[className] = s;
|
|
}
|
|
|
|
void AddHeaderForward(string className, string fwd) {
|
|
if (fwd == className) return;
|
|
if (!_headerForwards.TryGetValue(className, out var set))
|
|
_headerForwards[className] = set = new(StringComparer.Ordinal);
|
|
set.Add(fwd);
|
|
}
|
|
|
|
void AddCppInclude(string className, string need) {
|
|
if (need == className) return;
|
|
if (!_cppIncludes.TryGetValue(className, out var set))
|
|
_cppIncludes[className] = set = new(StringComparer.Ordinal);
|
|
set.Add(need);
|
|
}
|
|
|
|
// =========================================
|
|
// VulkanFunctions — PFN wrappers + loaders with platform guards
|
|
// =========================================
|
|
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("#include <utility>");
|
|
sbH.AppendLine();
|
|
sbH.AppendLine("namespace VulkanPP {");
|
|
sbH.AppendLine();
|
|
sbH.AppendLine("// Small callable wrapper around PFN_vk* that throws if not loaded.");
|
|
sbH.AppendLine("template <typename PFN>");
|
|
sbH.AppendLine("struct Fn {");
|
|
sbH.AppendLine(" PFN p = nullptr;");
|
|
sbH.AppendLine(" const char* name = nullptr;");
|
|
sbH.AppendLine(" bool loaded() const noexcept { return p != nullptr; }");
|
|
sbH.AppendLine(" template <typename... Args>");
|
|
sbH.AppendLine(" auto operator()(Args&&... args) const");
|
|
sbH.AppendLine(" -> decltype(p(std::forward<Args>(args)...))");
|
|
sbH.AppendLine(" {");
|
|
sbH.AppendLine(" if (!p) throw std::runtime_error(std::string(\"Vulkan function not loaded: \") + (name ? name : \"<null>\"));");
|
|
sbH.AppendLine(" return p(std::forward<Args>(args)...);");
|
|
sbH.AppendLine(" }");
|
|
sbH.AppendLine("};");
|
|
sbH.AppendLine();
|
|
sbH.AppendLine("struct VulkanFunctions {");
|
|
|
|
foreach (var cmd in EnabledCommands().OrderBy(c => c.Name)) {
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
if (guard.Length > 0) sbH.AppendLine("#if " + guard);
|
|
sbH.AppendLine(" Fn<PFN_" + cmd.Name + "> " + cmd.Name + ";");
|
|
if (guard.Length > 0) sbH.AppendLine("#endif");
|
|
}
|
|
|
|
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;");
|
|
sbH.AppendLine();
|
|
sbH.AppendLine("} // namespace VulkanPP");
|
|
File.WriteAllText(Path.Combine(_outDir, "VulkanFunctions.hpp"), sbH.ToString());
|
|
|
|
var sbCpp = new StringBuilder();
|
|
sbCpp.AppendLine("#include <VulkanPP/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();
|
|
sbCpp.AppendLine("namespace VulkanPP {");
|
|
sbCpp.AppendLine();
|
|
sbCpp.AppendLine("VulkanFunctions vk;");
|
|
sbCpp.AppendLine();
|
|
sbCpp.AppendLine("void VulkanFunctions::loadGlobal(){");
|
|
sbCpp.AppendLine(" vkGetInstanceProcAddr.p = (PFN_vkGetInstanceProcAddr)LoadSymbol(\"vkGetInstanceProcAddr\");");
|
|
sbCpp.AppendLine(" vkGetInstanceProcAddr.name = \"vkGetInstanceProcAddr\";");
|
|
|
|
foreach (var cmd in EnabledCommands()
|
|
.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Global)
|
|
.OrderBy(c => c.Name)) {
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
if (guard.Length > 0) sbCpp.AppendLine("#if " + guard);
|
|
sbCpp.AppendLine(" " + cmd.Name + ".p = (PFN_" + cmd.Name + ")vkGetInstanceProcAddr.p(nullptr, \"" + cmd.Name + "\");");
|
|
sbCpp.AppendLine(" " + cmd.Name + ".name = \"" + cmd.Name + "\";");
|
|
if (guard.Length > 0) sbCpp.AppendLine("#endif");
|
|
}
|
|
sbCpp.AppendLine("}");
|
|
sbCpp.AppendLine();
|
|
sbCpp.AppendLine("void VulkanFunctions::loadInstance(VkInstance instance){");
|
|
foreach (var cmd in EnabledCommands()
|
|
.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Instance)
|
|
.OrderBy(c => c.Name)) {
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
if (guard.Length > 0) sbCpp.AppendLine("#if " + guard);
|
|
sbCpp.AppendLine(" " + cmd.Name + ".p = (PFN_" + cmd.Name + ")vkGetInstanceProcAddr.p(instance, \"" + cmd.Name + "\");");
|
|
sbCpp.AppendLine(" " + cmd.Name + ".name = \"" + cmd.Name + "\";");
|
|
if (guard.Length > 0) sbCpp.AppendLine("#endif");
|
|
}
|
|
sbCpp.AppendLine("}");
|
|
sbCpp.AppendLine();
|
|
sbCpp.AppendLine("void VulkanFunctions::loadDevice(VkDevice device){");
|
|
sbCpp.AppendLine(" vkGetDeviceProcAddr.p = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr.p(nullptr, \"vkGetDeviceProcAddr\");");
|
|
sbCpp.AppendLine(" vkGetDeviceProcAddr.name = \"vkGetDeviceProcAddr\";");
|
|
foreach (var cmd in EnabledCommands()
|
|
.Where(c => _spec.ClassifyLoadLevel(c) == VkSpec.LoadLevel.Device)
|
|
.OrderBy(c => c.Name)) {
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
if (guard.Length > 0) sbCpp.AppendLine("#if " + guard);
|
|
sbCpp.AppendLine(" " + cmd.Name + ".p = (PFN_" + cmd.Name + ")vkGetDeviceProcAddr.p(device, \"" + cmd.Name + "\");");
|
|
sbCpp.AppendLine(" " + cmd.Name + ".name = \"" + cmd.Name + "\";");
|
|
if (guard.Length > 0) sbCpp.AppendLine("#endif");
|
|
}
|
|
sbCpp.AppendLine("}");
|
|
sbCpp.AppendLine();
|
|
sbCpp.AppendLine("} // namespace VulkanPP");
|
|
File.WriteAllText(Path.Combine(_outDir, "VulkanFunctions.cpp"), sbCpp.ToString());
|
|
}
|
|
|
|
// =========================================
|
|
// 1) Skeletons (.hpp classes) + ctor/dtor declarations
|
|
// =========================================
|
|
void GenerateHandleWrappersSkeletons() {
|
|
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);
|
|
TouchHeader(className);
|
|
|
|
VkSpec.Pair? pair = _spec.Pairs.FirstOrDefault(p => p.HandleType == handleType);
|
|
var hasOwned = pair != null;
|
|
|
|
var primaryOwners = new List<string>();
|
|
if (hasOwned) {
|
|
if (!string.IsNullOrEmpty(pair!.ParentType))
|
|
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();
|
|
}
|
|
|
|
// record forward-decls (owners + lifetime-kept types)
|
|
foreach (var o in primaryOwners) AddHeaderForward(className, ToClass(o));
|
|
foreach (var u in lifetimeUses) AddHeaderForward(className, ToClass(u.Type));
|
|
|
|
// record cpp includes for destructor/ctor usage
|
|
foreach (var o in primaryOwners) AddCppInclude(className, ToClass(o));
|
|
foreach (var u in lifetimeUses) AddCppInclude(className, ToClass(u.Type));
|
|
|
|
// Header class skeleton
|
|
var header = _headers[className];
|
|
|
|
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; }");
|
|
|
|
// owners getters
|
|
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 + "; }");
|
|
}
|
|
|
|
// fromHandle stays inline (safe)
|
|
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(" }");
|
|
|
|
// Private ctor/dtor declarations
|
|
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(");");
|
|
header.AppendLine(" ~" + className + "() override;");
|
|
|
|
// fields
|
|
header.AppendLine("private:");
|
|
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("};");
|
|
header.AppendLine("using P" + className + " = IntrusivePtr<" + className + ">;");
|
|
header.AppendLine();
|
|
header.AppendLine("} // namespace VulkanPP");
|
|
|
|
// .cpp: ctor definition
|
|
var src = _sources[className];
|
|
src.Append(" " + className + "::" + className + "(");
|
|
for (int i = 0; i < primaryOwners.Count; i++) {
|
|
if (i > 0) src.Append(", ");
|
|
src.Append("IntrusivePtr<" + ToClass(primaryOwners[i]) + "> owner" + i);
|
|
}
|
|
if (primaryOwners.Count > 0) src.Append(", ");
|
|
src.Append(handleType + " handle, bool owned");
|
|
foreach (var u in lifetimeUses) {
|
|
src.Append(", ");
|
|
var cls = ToClass(u.Type);
|
|
src.Append(u.IsMany
|
|
? "const std::vector<IntrusivePtr<" + cls + ">>& owners_" + cls
|
|
: "IntrusivePtr<" + cls + "> owner_" + cls);
|
|
}
|
|
src.AppendLine(")");
|
|
src.AppendLine(" : _handle(handle), _owned(owned)");
|
|
for (int i = 0; i < primaryOwners.Count; i++)
|
|
src.AppendLine(" , _owner" + i + "(std::move(owner" + i + "))");
|
|
foreach (var u in lifetimeUses) {
|
|
var cls = ToClass(u.Type);
|
|
src.AppendLine(u.IsMany
|
|
? " , _lifetime_owners_" + cls + "(owners_" + cls + ")"
|
|
: " , _lifetime_owner_" + cls + "(std::move(owner_" + cls + "))");
|
|
}
|
|
src.AppendLine("{ }");
|
|
src.AppendLine();
|
|
|
|
// .cpp: dtor definition
|
|
src.AppendLine(" " + className + "::~" + className + "() {");
|
|
src.AppendLine(" if (_handle && _owned) {");
|
|
if (hasOwned && !string.IsNullOrEmpty(pair!.DestroyOrFree)
|
|
&& _spec.Commands.TryGetValue(pair!.DestroyOrFree, out var destroyCmd)) {
|
|
var guard = GuardExpr(destroyCmd.ProtectMacros);
|
|
if (guard.Length > 0) src.AppendLine("#if " + guard);
|
|
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} ? _owner{ownerIdx}->getHandle() : ({p.Type})VK_NULL_HANDLE");
|
|
ownerIdx++;
|
|
continue;
|
|
}
|
|
if (p.Name.EndsWith("Count")) args.Add("0"); else args.Add("0");
|
|
}
|
|
src.AppendLine(" (void)vk." + destroyCmd.Name + "(" + string.Join(", ", args) + ");");
|
|
src.AppendLine(" _handle = VK_NULL_HANDLE;");
|
|
if (guard.Length > 0) src.AppendLine("#endif");
|
|
}
|
|
src.AppendLine(" }");
|
|
src.AppendLine("}");
|
|
src.AppendLine();
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// 2) Factories: ctor / createMany (prototypes inside class) + bodies in .cpp
|
|
// =========================================
|
|
void GenerateFactories() {
|
|
foreach (var pair in _spec.Pairs) {
|
|
var handleType = pair.HandleType;
|
|
var className = ToClass(handleType);
|
|
var src = _sources[className];
|
|
|
|
var primaryOwners = new List<string>();
|
|
if (!string.IsNullOrEmpty(pair.ParentType)) primaryOwners.Add(pair.ParentType);
|
|
primaryOwners.AddRange(pair.ExtraOwnersForDestroy.Where(o => o != pair.ParentType));
|
|
primaryOwners = primaryOwners.Distinct().ToList();
|
|
|
|
var createCmd = _spec.Commands[pair.CreateOrAllocate];
|
|
var lifetimeUses = _spec.AnalyzeCreateInfoOwners(createCmd, handleType)
|
|
.Where(u => !primaryOwners.Contains(u.Type))
|
|
.ToList();
|
|
|
|
bool multiOut = pair.OutHandleParam.LenAttr != null;
|
|
bool hasParent = !string.IsNullOrEmpty(pair.ParentType);
|
|
var parentClass = hasParent ? ToClass(pair.ParentType) : "";
|
|
|
|
// signature pieces
|
|
var ps = createCmd.Params.ToList();
|
|
var outLenName = pair.OutHandleParam.LenAttr;
|
|
var lenNamesToSkip = new HashSet<string>();
|
|
var sigParams = new List<string>();
|
|
var prep = new StringBuilder();
|
|
var callArgsPrepared = new Queue<string>();
|
|
|
|
if (hasParent) { sigParams.Add($"IntrusivePtr<{parentClass}> parent"); AddHeaderForward(className, parentClass); AddCppInclude(className, parentClass); }
|
|
|
|
foreach (var p in ps) {
|
|
if (hasParent && p == ps.First()) continue;
|
|
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);
|
|
AddHeaderForward(className, cls);
|
|
AddCppInclude(className, cls);
|
|
|
|
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!);
|
|
callArgsPrepared.Enqueue($"{p.Name}.size()");
|
|
callArgsPrepared.Enqueue($"{rawVec}.data()");
|
|
} else {
|
|
sigParams.Add($"IntrusivePtr<{cls}> {p.Name}");
|
|
callArgsPrepared.Enqueue($"{p.Name}->getHandle()");
|
|
}
|
|
} else {
|
|
if (lenNamesToSkip.Contains(p.Name)) continue;
|
|
sigParams.Add($"{(p.IsConst ? "const " : "")}{p.Type}{(p.IsPointer ? "*" : "")} {p.Name}");
|
|
callArgsPrepared.Enqueue(p.Name);
|
|
}
|
|
}
|
|
|
|
foreach (var u in lifetimeUses) {
|
|
var cls = ToClass(u.Type);
|
|
AddHeaderForward(className, cls);
|
|
AddCppInclude(className, cls);
|
|
sigParams.Add(u.IsMany
|
|
? $"const std::vector<IntrusivePtr<{cls}>>& owners_{cls}"
|
|
: $"IntrusivePtr<{cls}> owner_{cls}");
|
|
}
|
|
|
|
string ResolveOwnerExpr(string ownerVkType) {
|
|
if (ownerVkType == pair.ParentType && hasParent) return "parent";
|
|
var pFound = createCmd.Params.FirstOrDefault(pp => pp.IsHandle && pp.Type == ownerVkType
|
|
&& (!pp.IsPointer || pp.LenAttr == null)
|
|
&& (!hasParent || pp != createCmd.Params.First()));
|
|
if (pFound != null) return pFound.Name;
|
|
var lu = lifetimeUses.FirstOrDefault(x => x.Type == ownerVkType && !x.IsMany);
|
|
if (lu != null) return "owner_" + ToClass(ownerVkType);
|
|
return hasParent ? "parent" : $"({ToClass(ownerVkType)})nullptr";
|
|
}
|
|
|
|
var guardCreate = GuardExpr(createCmd.ProtectMacros);
|
|
|
|
if (!multiOut) {
|
|
// ---------- DECLARE: constructor (inside class) ----------
|
|
var decl = new StringBuilder();
|
|
if (guardCreate.Length > 0) decl.AppendLine("#if " + guardCreate);
|
|
decl.AppendLine("public:");
|
|
decl.AppendLine("// Original: " + createCmd.Name);
|
|
decl.AppendLine(" explicit " + className + "(" + string.Join(", ", sigParams) + ");");
|
|
// ---------- DECLARE: static create(...) (inside class) ----------
|
|
decl.AppendLine("// Original: " + createCmd.Name + " (factory)");
|
|
decl.AppendLine(" static IntrusivePtr<" + className + "> create(" + string.Join(", ", sigParams) + ");");
|
|
if (guardCreate.Length > 0) decl.AppendLine("#endif");
|
|
decl.AppendLine();
|
|
AddToHeaderClass(className, decl.ToString());
|
|
|
|
// ---------- DEFINE: constructor in .cpp ----------
|
|
if (guardCreate.Length > 0) src.AppendLine("#if " + guardCreate);
|
|
src.AppendLine("// Original: " + createCmd.Name);
|
|
src.Append(className + "::" + className + "(" + string.Join(", ", sigParams) + ")");
|
|
src.AppendLine(" : _handle(VK_NULL_HANDLE), _owned(true)");
|
|
for (int i = 0; i < primaryOwners.Count; ++i)
|
|
src.AppendLine(" , _owner" + i + "(" + ResolveOwnerExpr(primaryOwners[i]) + ")");
|
|
foreach (var u in lifetimeUses) {
|
|
var cls = ToClass(u.Type);
|
|
src.AppendLine(u.IsMany
|
|
? " , _lifetime_owners_" + cls + "(owners_" + cls + ")"
|
|
: " , _lifetime_owner_" + cls + "(owner_" + cls + ")");
|
|
}
|
|
src.AppendLine("{");
|
|
src.Append(prep.ToString());
|
|
|
|
var outName = "_out";
|
|
src.AppendLine(" " + pair.HandleType + " " + outName + " = VK_NULL_HANDLE;");
|
|
|
|
var argsFinal = new List<string>();
|
|
foreach (var p in createCmd.Params) {
|
|
if (hasParent && p == createCmd.Params.First()) { argsFinal.Add("parent->getHandle()"); continue; }
|
|
if (!hasParent && p == createCmd.Params.First() && !p.IsHandle) { /* queued above */ }
|
|
if (p.Name == "pAllocator") { argsFinal.Add("nullptr"); continue; }
|
|
if (p.IsPointer && p.Type == pair.HandleType) { argsFinal.Add("&" + outName); continue; }
|
|
if (outLenName != null && p.Name == outLenName) { argsFinal.Add("0"); continue; }
|
|
if (callArgsPrepared.Count > 0) argsFinal.Add(callArgsPrepared.Dequeue());
|
|
}
|
|
|
|
src.AppendLine(" auto _res = vk." + createCmd.Name + "(" + string.Join(", ", argsFinal) + ");");
|
|
src.AppendLine(" if (_res != VK_SUCCESS) throw std::runtime_error(\"" + createCmd.Name + " failed\");");
|
|
src.AppendLine(" _handle = " + outName + ";");
|
|
src.AppendLine("}");
|
|
|
|
// ---------- DEFINE: static create(...) in .cpp ----------
|
|
// derive argument names from the signature we just built
|
|
var argNames = sigParams
|
|
.Select(p => {
|
|
// works for: "IntrusivePtr<T> x", "const VkFoo* p", "const std::vector<...>& arr", etc.
|
|
var parts = p.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
return parts[^1];
|
|
})
|
|
.ToList();
|
|
|
|
src.AppendLine("// Original: " + createCmd.Name + " (factory)");
|
|
src.Append("IntrusivePtr<" + className + "> " + className + "::create(" + string.Join(", ", sigParams) + ")");
|
|
src.AppendLine(" {");
|
|
src.Append(" return IntrusivePtr<" + className + ">(new " + className + "(" + string.Join(", ", argNames) + "));");
|
|
src.AppendLine();
|
|
src.AppendLine("}");
|
|
if (guardCreate.Length > 0) src.AppendLine("#endif");
|
|
src.AppendLine();
|
|
} else {
|
|
// declare static createMany **inside class**
|
|
var decl = new StringBuilder();
|
|
if (guardCreate.Length > 0) decl.AppendLine("#if " + guardCreate);
|
|
decl.AppendLine("public:");
|
|
decl.AppendLine("// Original: " + createCmd.Name);
|
|
decl.AppendLine(" static std::vector<IntrusivePtr<" + className + ">> createMany(" + string.Join(", ", sigParams) + ");");
|
|
if (guardCreate.Length > 0) decl.AppendLine("#endif");
|
|
decl.AppendLine();
|
|
AddToHeaderClass(className, decl.ToString());
|
|
|
|
// define in .cpp
|
|
if (guardCreate.Length > 0) src.AppendLine("#if " + guardCreate);
|
|
src.AppendLine("// Original: " + createCmd.Name);
|
|
src.Append("std::vector<IntrusivePtr<" + className + ">> " + className + "::createMany(" + string.Join(", ", sigParams) + ")");
|
|
src.AppendLine(" {");
|
|
src.Append(prep.ToString());
|
|
var outName = "_out";
|
|
var lenName = outLenName ?? "count";
|
|
src.AppendLine(" std::vector<" + pair.HandleType + "> " + outName + ";");
|
|
src.AppendLine(" " + outName + ".resize(" + lenName + ");");
|
|
|
|
var argsFinal = new List<string>();
|
|
foreach (var p in createCmd.Params) {
|
|
if (hasParent && p == createCmd.Params.First()) { argsFinal.Add("parent->getHandle()"); continue; }
|
|
if (p.Name == "pAllocator") { argsFinal.Add("nullptr"); continue; }
|
|
if (p.IsPointer && p.Type == pair.HandleType) { argsFinal.Add(outName + ".data()"); continue; }
|
|
if (outLenName != null && p.Name == outLenName) { argsFinal.Add(lenName); continue; }
|
|
if (callArgsPrepared.Count > 0) argsFinal.Add(callArgsPrepared.Dequeue());
|
|
}
|
|
|
|
src.AppendLine(" auto _res = vk." + createCmd.Name + "(" + string.Join(", ", argsFinal) + ");");
|
|
src.AppendLine(" if (_res != VK_SUCCESS) throw std::runtime_error(\"" + createCmd.Name + " failed\");");
|
|
src.AppendLine(" std::vector<IntrusivePtr<" + className + ">> _ret; _ret.reserve(" + outName + ".size());");
|
|
src.AppendLine(" for (auto h : " + outName + ") {");
|
|
var ctorOwners = new List<string>();
|
|
for (int i = 0; i < primaryOwners.Count; ++i) ctorOwners.Add(ResolveOwnerExpr(primaryOwners[i]));
|
|
|
|
src.Append(" _ret.emplace_back(new " + className + "(" + string.Join(", ", ctorOwners));
|
|
if (primaryOwners.Count > 0) src.Append(", ");
|
|
src.Append("h, true");
|
|
foreach (var u in lifetimeUses) {
|
|
var cls = ToClass(u.Type);
|
|
src.Append(", ");
|
|
src.Append(u.IsMany ? "owners_" + cls : "owner_" + cls);
|
|
}
|
|
src.AppendLine("));");
|
|
src.AppendLine(" }");
|
|
src.AppendLine(" return _ret;");
|
|
src.AppendLine("}");
|
|
if (guardCreate.Length > 0) src.AppendLine("#endif");
|
|
src.AppendLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// 3) Producer methods (Get*/Enumerate*)
|
|
// =========================================
|
|
void GenerateProducerMemberMethods() {
|
|
foreach (var pr in _spec.Producers) {
|
|
var parentClass = ToClass(pr.ParentType);
|
|
var childClass = ToClass(pr.HandleType);
|
|
TouchHeader(parentClass);
|
|
|
|
var cmd = _spec.Commands[pr.CommandName];
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
|
|
AddHeaderForward(parentClass, childClass);
|
|
AddCppInclude(parentClass, childClass);
|
|
foreach (var p in cmd.Params.Skip(1))
|
|
if (p.IsHandle) { AddHeaderForward(parentClass, ToClass(p.Type)); AddCppInclude(parentClass, ToClass(p.Type)); }
|
|
|
|
var outParam = pr.OutHandleParam;
|
|
var lenName = outParam.LenAttr;
|
|
|
|
var sigParams = new List<string>();
|
|
foreach (var p in cmd.Params.Skip(1)) {
|
|
if (p.Name == outParam.Name) continue;
|
|
if (!string.IsNullOrEmpty(lenName) && p.Name == lenName) continue;
|
|
if (p.IsHandle)
|
|
sigParams.Add("IntrusivePtr<" + ToClass(p.Type) + "> " + p.Name);
|
|
else
|
|
sigParams.Add((p.IsConst ? "const " : "") + p.Type + (p.IsPointer ? "*" : "") + " " + p.Name);
|
|
}
|
|
|
|
var retWrapper = pr.IsMany
|
|
? "std::vector<IntrusivePtr<" + childClass + ">>"
|
|
: "IntrusivePtr<" + childClass + ">";
|
|
|
|
var methodName = "get" + childClass + (pr.IsMany ? "s" : "");
|
|
|
|
// header prototype **inside class**
|
|
var decl = new StringBuilder();
|
|
if (guard.Length > 0) decl.AppendLine("#if " + guard);
|
|
decl.AppendLine("public:");
|
|
decl.AppendLine("// Original: " + cmd.Name);
|
|
decl.AppendLine(" " + retWrapper + " " + methodName + "(" + string.Join(", ", sigParams) + ");");
|
|
if (guard.Length > 0) decl.AppendLine("#endif");
|
|
decl.AppendLine();
|
|
AddToHeaderClass(parentClass, decl.ToString());
|
|
|
|
// Source definition
|
|
var src = _sources[parentClass];
|
|
if (guard.Length > 0) src.AppendLine("#if " + guard);
|
|
src.AppendLine("// Original: " + cmd.Name);
|
|
src.Append(retWrapper + " " + parentClass + "::" + methodName + "(" + string.Join(", ", sigParams) + ")");
|
|
src.AppendLine(" {");
|
|
|
|
if (pr.IsMany) {
|
|
var countParam = cmd.Params.First(p => p.Name == lenName);
|
|
src.AppendLine(" " + countParam.Type + " _count = 0;");
|
|
|
|
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") {
|
|
src.AppendLine(" auto _r1 = " + call1 + ";");
|
|
src.AppendLine(" if (_r1 != VK_SUCCESS && _r1 != VK_INCOMPLETE) throw std::runtime_error(\"" + cmd.Name + " (count) failed\");");
|
|
} else src.AppendLine(" " + call1 + ";");
|
|
|
|
src.AppendLine(" std::vector<" + pr.HandleType + "> _out; _out.resize(_count);");
|
|
|
|
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") {
|
|
src.AppendLine(" auto _r2 = " + call2 + ";");
|
|
src.AppendLine(" if (_r2 != VK_SUCCESS) throw std::runtime_error(\"" + cmd.Name + " (fill) failed\");");
|
|
} else src.AppendLine(" " + call2 + ";");
|
|
|
|
src.AppendLine(" std::vector<IntrusivePtr<" + childClass + ">> _ret; _ret.reserve(_out.size());");
|
|
src.AppendLine(" IntrusivePtr<" + parentClass + "> _self(this);");
|
|
src.AppendLine(" for (auto h : _out) { _ret.emplace_back(new " + childClass + "(_self, h, false)); }");
|
|
src.AppendLine(" return _ret;");
|
|
} else {
|
|
src.AppendLine(" " + pr.HandleType + " _h = VK_NULL_HANDLE;");
|
|
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") {
|
|
src.AppendLine(" auto _r = " + call + ";");
|
|
src.AppendLine(" if (_r != VK_SUCCESS) throw std::runtime_error(\"" + cmd.Name + " failed\");");
|
|
} else src.AppendLine(" " + call + ";");
|
|
|
|
src.AppendLine(" return IntrusivePtr<" + childClass + ">(new " + childClass + "(IntrusivePtr<" + parentClass + ">(this), _h, false));");
|
|
}
|
|
|
|
src.AppendLine("}");
|
|
if (guard.Length > 0) src.AppendLine("#endif");
|
|
src.AppendLine();
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// 4) Non-producer member methods
|
|
// =========================================
|
|
void GenerateMemberMethods() {
|
|
foreach (var cmd in EnabledCommands()) {
|
|
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);
|
|
TouchHeader(className);
|
|
|
|
var guard = GuardExpr(cmd.ProtectMacros);
|
|
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);
|
|
AddHeaderForward(className, cls);
|
|
AddCppInclude(className, cls);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// header prototype inside class
|
|
var decl = new StringBuilder();
|
|
if (guard.Length > 0) decl.AppendLine("#if " + guard);
|
|
decl.AppendLine("public:");
|
|
decl.AppendLine("// Original: " + cmd.Name);
|
|
decl.AppendLine(" " + retType + " " + methodName + "(" + string.Join(", ", sigParams) + ");");
|
|
if (guard.Length > 0) decl.AppendLine("#endif");
|
|
decl.AppendLine();
|
|
AddToHeaderClass(className, decl.ToString());
|
|
|
|
// source definition
|
|
var src = _sources[className];
|
|
if (guard.Length > 0) src.AppendLine("#if " + guard);
|
|
src.AppendLine("// Original: " + cmd.Name);
|
|
src.Append(retType + " " + className + "::" + methodName + "(" + string.Join(", ", sigParams) + ")");
|
|
src.AppendLine(" {");
|
|
src.Append(prep.ToString());
|
|
src.Append(" ");
|
|
if (retType != "void") src.Append("return ");
|
|
var call = "vk." + cmd.Name + "(_handle";
|
|
if (callArgs.Count > 0) call += ", " + string.Join(", ", callArgs);
|
|
call += ");";
|
|
src.AppendLine(call);
|
|
src.AppendLine("}");
|
|
if (guard.Length > 0) src.AppendLine("#endif");
|
|
src.AppendLine();
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// 5) Sugar methods (prototypes inside class + bodies)
|
|
// =========================================
|
|
void GenerateSugarMethods() {
|
|
foreach (var kv in _sugar) {
|
|
var className = kv.Key;
|
|
if (!_headers.ContainsKey(className)) TouchHeader(className);
|
|
|
|
var spec = kv.Value;
|
|
foreach (var m in spec.Methods) {
|
|
if (!m.RequiresFunctions.All(fn => _spec.Commands.ContainsKey(fn))) continue;
|
|
if (!m.RequiresExtensions.All(ext => _spec.Extensions.ContainsKey(ext))) continue;
|
|
|
|
foreach (var p in m.Parameters) {
|
|
var m1 = Regex.Match(p, @"IntrusivePtr<\s*(\w+)\s*>");
|
|
if (m1.Success) { AddHeaderForward(className, m1.Groups[1].Value); AddCppInclude(className, m1.Groups[1].Value); }
|
|
var m2 = Regex.Match(p, @"std::vector\s*<\s*IntrusivePtr<\s*(\w+)\s*>\s*>");
|
|
if (m2.Success) { AddHeaderForward(className, m2.Groups[1].Value); AddCppInclude(className, m2.Groups[1].Value); }
|
|
}
|
|
|
|
var (sig, prelude, vars, guard) = BuildSugarSignatureAndContext(m, className);
|
|
|
|
var decl = new StringBuilder();
|
|
if (!string.IsNullOrEmpty(guard)) decl.AppendLine("#if " + guard);
|
|
decl.AppendLine("public:");
|
|
decl.AppendLine("// Original: (sugar)");
|
|
decl.AppendLine(" " + sig + ";");
|
|
if (!string.IsNullOrEmpty(guard)) decl.AppendLine("#endif");
|
|
decl.AppendLine();
|
|
AddToHeaderClass(className, decl.ToString());
|
|
|
|
var src = _sources[className];
|
|
if (!string.IsNullOrEmpty(guard)) src.AppendLine("#if " + guard);
|
|
src.AppendLine("// Original: (sugar)");
|
|
var nameOnly = sig.Substring(sig.IndexOf(' ') + 1);
|
|
var idxParen = nameOnly.IndexOf('(');
|
|
var methodName = nameOnly.Substring(0, idxParen);
|
|
var argsIn = nameOnly.Substring(idxParen);
|
|
src.Append(m.ReturnType + " " + className + "::" + methodName + argsIn);
|
|
src.AppendLine(" {");
|
|
if (!string.IsNullOrEmpty(prelude))
|
|
foreach (var line in prelude.Split('\n'))
|
|
if (line.Length > 0) src.AppendLine(" " + line);
|
|
foreach (var ln in m.BodyLines)
|
|
src.AppendLine(" " + Render(ln, vars));
|
|
src.AppendLine("}");
|
|
if (!string.IsNullOrEmpty(guard)) src.AppendLine("#endif");
|
|
src.AppendLine();
|
|
}
|
|
}
|
|
|
|
(string sig, string prelude, Dictionary<string, string> vars, string guard)
|
|
BuildSugarSignatureAndContext(SugarMethodSpec m, string className) {
|
|
var vars = new Dictionary<string, string> {
|
|
["class"] = className,
|
|
["handle"] = "_handle",
|
|
["vk"] = "vk"
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
static string Render(string tpl, Dictionary<string, string> vars) {
|
|
foreach (var kv in vars) tpl = tpl.Replace("${" + kv.Key + "}", kv.Value);
|
|
return tpl;
|
|
}
|
|
}
|
|
|
|
// =========================================
|
|
// 6) Finalize: add forwards/includes, close namespaces, write files
|
|
// =========================================
|
|
void FinalizeHeadersAndSources() {
|
|
foreach (var (className, h) in _headers.ToArray()) {
|
|
var text = h.ToString();
|
|
|
|
if (_headerForwards.TryGetValue(className, out var fwds) && fwds.Count > 0) {
|
|
var block = new StringBuilder();
|
|
foreach (var n in fwds.OrderBy(n => n))
|
|
block.AppendLine("class " + n + ";");
|
|
|
|
int posNs = text.IndexOf("namespace VulkanPP {", StringComparison.Ordinal);
|
|
if (posNs >= 0) {
|
|
int insertPos = posNs + "namespace VulkanPP {".Length;
|
|
text = text.Insert(insertPos, "\n" + block.ToString() + "\n");
|
|
} else {
|
|
text = block.ToString() + "\n" + text;
|
|
}
|
|
}
|
|
|
|
if (!text.EndsWith("\n")) text += "\n";
|
|
File.WriteAllText(Path.Combine(_outDir, className + ".hpp"), text);
|
|
}
|
|
|
|
foreach (var (className, s) in _sources.ToArray()) {
|
|
var includes = new StringBuilder();
|
|
if (_cppIncludes.TryGetValue(className, out var incs)) {
|
|
foreach (var n in incs.OrderBy(n => n))
|
|
includes.AppendLine("#include <VulkanPP/" + n + ".hpp>");
|
|
includes.AppendLine();
|
|
}
|
|
|
|
var text = s.ToString();
|
|
int firstNL = text.IndexOf('\n');
|
|
if (firstNL >= 0) text = text.Insert(firstNL + 1, includes.ToString());
|
|
|
|
if (!text.EndsWith("\n} // namespace VulkanPP\n")) {
|
|
if (!text.EndsWith("\n")) text += "\n";
|
|
text += "} // namespace VulkanPP\n";
|
|
}
|
|
|
|
File.WriteAllText(Path.Combine(_outDir, className + ".cpp"), text);
|
|
}
|
|
}
|
|
}
|