Mercurial > games > semicongine
diff src/vulkan_api/vulkan_api_generator.nim @ 83:5e19aead2b61
add: new vulkan api wrapper, not done yet
author | Sam <sam@basx.dev> |
---|---|
date | Thu, 23 Feb 2023 00:34:38 +0700 |
parents | fa1b6107deae |
children | 8412f433dc46 |
line wrap: on
line diff
--- a/src/vulkan_api/vulkan_api_generator.nim Wed Feb 22 18:36:26 2023 +0700 +++ b/src/vulkan_api/vulkan_api_generator.nim Thu Feb 23 00:34:38 2023 +0700 @@ -59,6 +59,15 @@ SPECIAL_DEPENDENCIES = { "VK_NV_ray_tracing": "VK_KHR_ray_tracing_pipeline", }.toTable + # will be directly loaded at startup + IGNORED_COMMANDS = @["vkGetInstanceProcAddr"] + # can be loaded without a vulkan instance + GLOBAL_COMMANDS = @[ + "vkEnumerateInstanceVersion", + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceLayerProperties", + "vkCreateInstance", + ] # helpers func mapType(typename: string): auto = @@ -207,9 +216,22 @@ if not member.hasAttr("api") or member.attr("api") == "vulkan": let fieldname = member.child("name")[0].text.strip(chars={'_'}) var fieldtype = member.child("type")[0].text.strip(chars={'_'}) - if member[member.len - 2].kind == xnText and member[member.len - 2].text.strip() == "*": - fieldtype = &"ptr {mapType(fieldtype)}" + # detect pointers + for child in member: + if child.kind == xnText and child.text.strip() == "*": + fieldtype = &"ptr {mapType(fieldtype)}" + elif child.kind == xnText and child.text.strip() == "* const*": + fieldtype = "cstringArray" fieldtype = mapType(fieldtype) + # detect arrays + for child in member: + if child.kind == xnText and child.text.endsWith("]"): + var thelen = "" + if "[" in child.text: + thelen = child.text.strip(chars={'[', ']'}).replace("][", "*") + else: + thelen = member.child("enum")[0].text + fieldtype = &"array[{thelen}, {fieldtype}]" result.add &" {mapName(fieldname)}*: {fieldtype}" func serializeFunctiontypes(api: XmlNode): seq[string] = @@ -229,6 +251,23 @@ let paramsstr = params.join(", ") result.add(&" {name}* = proc({paramsstr}): {returntype} {{.cdecl.}}") +func serializeConsts(api: XmlNode): seq[string] = + result = @["const"] + for enums in api.findAll("enums"): + if enums.attr("name") == "API Constants": + for theenum in enums.findAll("enum"): + if theenum.hasAttr("alias"): + result.add &" {theenum.attr(\"name\")}* = {theenum.attr(\"alias\")}" + else: + var value = theenum.attr("value").strip(chars={'(', ')'}) + if value.endsWith("U"): + value = value[0..^2] & "'u32" + elif value.endsWith("ULL"): + value = value[0..^4] & "'u64" + if value[0] == '~': + value = "not " & value[1..^1] + result.add &" {theenum.attr(\"name\")}*: {mapType(theenum.attr(\"type\"))} = {value}" + func serializeType(node: XmlNode, headerTypes: Table[string, string]): Table[string, seq[string]] = if node.attrsLen == 0: return @@ -243,7 +282,7 @@ let platformfile = "platform/" & platform if not result.hasKey(platformfile): result[platformfile] = @[] - result[platformfile].add " " & node.attr("name").strip(chars={'_'}) & " {.header: \"" & node.attr("requires") & "\".} = object" + result[platformfile].add " " & node.attr("name").strip(chars={'_'}) & " *{.header: \"" & node.attr("requires") & "\".} = object" # generic base types elif node.attr("category") == "basetype": let typechild = node.child("type") @@ -338,9 +377,10 @@ var outputFiles = { "basetypes": @[ "import std/dynlib", + "import std/tables", "type", - " VkHandle* = distinct pointer", - " VkNonDispatchableHandle* = distinct pointer", + " VkHandle* = distinct uint", + " VkNonDispatchableHandle* = distinct uint", "when defined(linux):", " let vulkanLib* = loadLib(\"libvulkan.so.1\")", "when defined(windows):", @@ -364,18 +404,21 @@ error "Vulkan error: ", astToStr(call), " returned ", $value raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value)""", - "type", ], "structs": @["type"], "enums": @["type"], "commands": @[], }.toTable + outputFiles["basetypes"].add serializeConsts(api) + outputFiles["basetypes"].add "type" # enums for thetype in api.findAll("type"): if thetype.attr("category") == "bitmask" and not thetype.hasAttr("alias") and (not thetype.hasAttr("api") or thetype.attr("api") == "vulkan"): let name = thetype.child("name")[0].text outputFiles["enums"].add &" {name}* = distinct VkFlags" + outputFiles["enums"].add "let vkGetInstanceProcAddr = cast[proc(instance: VkInstance, name: cstring): pointer {.stdcall.}](checkedSymAddr(vulkanLib, \"vkGetInstanceProcAddr\"))" + outputFiles["enums"].add "type" for theenum in api.findAll("enums"): outputFiles["enums"].add serializeEnum(theenum, api) @@ -408,7 +451,7 @@ var procLoads: Table[string, string] # procloads need to be packed into feature/extension loader procs for commands in api.findAll("commands"): for command in commands.findAll("command"): - if command.attr("api") != "vulkansc": + if command.attr("api") != "vulkansc" and not (command.attr("name") in IGNORED_COMMANDS): if command.hasAttr("alias"): let name = command.attr("name") let alias = command.attr("alias") @@ -418,7 +461,7 @@ else: let (name, thetype) = serializeCommand(command) varDecls[name] = &" {name}*: {thetype}" - procLoads[name] = &" {name} = cast[{thetype}](checkedSymAddr(vulkanLib, \"{name}\"))" + procLoads[name] = &" {name} = cast[{thetype}](vkGetInstanceProcAddr(instance, \"{name}\"))" var declared: seq[string] var featureloads: seq[string] for feature in api.findAll("feature"): @@ -427,17 +470,18 @@ outputFiles["commands"].add &"# feature {name}" outputFiles["commands"].add "var" for command in feature.findAll("command"): - if not (command.attr("name") in declared): + if not (command.attr("name") in declared) and not (command.attr("name") in IGNORED_COMMANDS): outputFiles["commands"].add varDecls[command.attr("name")] declared.add command.attr("name") featureloads.add &"load{name}" - outputFiles["commands"].add &"proc load{name}*() =" + outputFiles["commands"].add &"proc load{name}*(instance: VkInstance) =" for command in feature.findAll("command"): - outputFiles["commands"].add procLoads[command.attr("name")] + if not (command.attr("name") in IGNORED_COMMANDS & GLOBAL_COMMANDS): + outputFiles["commands"].add procLoads[command.attr("name")] outputFiles["commands"].add "" - outputFiles["commands"].add ["proc initVulkan*() ="] + outputFiles["commands"].add ["proc loadVulkan*(instance: VkInstance) ="] for l in featureloads: - outputFiles["commands"].add [&" {l}()"] + outputFiles["commands"].add [&" {l}(instance)"] outputFiles["commands"].add "" # for promoted extensions, dependants need to call the load-function of the promoted feature/extension @@ -464,6 +508,7 @@ if name in SPECIAL_DEPENDENCIES: extensionDependencies[name][0].add SPECIAL_DEPENDENCIES[name] + # order dependencies to generate them in correct order var dependencyOrderedExtensions: OrderedTable[string, XmlNode] while extensionDependencies.len > 0: var delkeys: seq[string] @@ -480,29 +525,34 @@ for key in delkeys: extensionDependencies.del key + var extensionLoaderMap: Table[string, Table[string, string]] for extension in dependencyOrderedExtensions.values: if extension.hasAttr("promotedto"): # will be loaded in promoted place continue if extension.attr("supported") in ["", "vulkan", "vulkan,vulkansc"]: var file = "commands" - if extension.attr("platform") != "": - file = "platform/" & extension.attr("platform") - elif extension.attr("name").startsWith("VK_KHR_video"): # hack since we do not include video headers by default - file = "platform/provisional" + var platform = extension.attr("platform") + if extension.attr("name").startsWith("VK_KHR_video"): + platform = "provisional" + if platform != "": + file = "platform/" & platform let name = extension.attr("name") if extension.findAll("command").len > 0: outputFiles[file].add &"# extension {name}" outputFiles[file].add "var" for command in extension.findAll("command"): - if not (command.attr("name") in declared): + if not (command.attr("name") in declared) and not (command.attr("name") in IGNORED_COMMANDS): outputFiles[file].add varDecls[command.attr("name")] declared.add command.attr("name") - outputFiles[file].add &"proc load{name}*() =" + outputFiles[file].add &"proc load{name}*(instance: VkInstance) =" + if not (platform in extensionLoaderMap): + extensionLoaderMap[platform] = Table[string, string]() + extensionLoaderMap[platform][name] = &"load{name}" var addedFunctionBody = false if extension.hasAttr("depends"): for dependency in extension.attr("depends").split("+"): # need to check since some extensions have no commands and therefore no load-function - outputFiles[file].add &" load{promotions.getOrDefault(dependency, dependency)}()" + outputFiles[file].add &" load{promotions.getOrDefault(dependency, dependency)}(instance)" addedFunctionBody = true for command in extension.findAll("command"): outputFiles[file].add procLoads[command.attr("name")] @@ -514,11 +564,31 @@ var mainout: seq[string] for section in ["basetypes", "enums", "structs", "commands"]: mainout.add outputFiles[section] + mainout.add "var EXTENSION_LOADERS = {" + for extension, loader in extensionLoaderMap[""].pairs: + mainout.add &" \"{extension}\": {loader}," + mainout.add "}.toTable" for platform in api.findAll("platform"): mainout.add &"when defined({platform.attr(\"protect\")}):" mainout.add &" include platform/{platform.attr(\"name\")}" + if platform.attr("name") in extensionLoaderMap: + for extension, loader in extensionLoaderMap[platform.attr("name")].pairs: + mainout.add &" EXTENSION_LOADERS[\"{extension}\"] = {loader}" + + mainout.add "" + mainout.add "proc loadExtension*(instance: VkInstance, extension: string) = EXTENSION_LOADERS[extension](instance)" + mainout.add "" + mainout.add "# load global functions immediately" + mainout.add "block globalFunctions:" + mainout.add " let instance = VkInstance(0)" + for l in GLOBAL_COMMANDS: + mainout.add procLoads[l] writeFile outdir / &"api.nim", mainout.join("\n") + mainout.add "" + mainout.add "converter VkBool2NimBool*(a: VkBool32): bool = a > 0" + mainout.add "converter NimBool2VkBool*(a: bool): VkBool32 = VkBool32(a)" + for filename, filecontent in outputFiles.pairs: if filename.startsWith("platform/"): writeFile outdir / &"{filename}.nim", (@[