Mercurial > games > semicongine
changeset 79:031f241de6ca
add: beta version
author | Sam <sam@basx.dev> |
---|---|
date | Wed, 22 Feb 2023 00:44:03 +0700 |
parents | f67496a189cb |
children | 637da715b604 |
files | src/vulkan_api/vulkan_api_generator.nim |
diffstat | 1 files changed, 198 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/src/vulkan_api/vulkan_api_generator.nim Mon Feb 20 10:33:53 2023 +0700 +++ b/src/vulkan_api/vulkan_api_generator.nim Wed Feb 22 00:44:03 2023 +0700 @@ -37,28 +37,31 @@ "VK_DEFINE_NON_DISPATCHABLE_HANDLE": "VkNonDispatchableHandle", }.toTable PLATFORM_HEADER_MAP = { - "X11/Xlib.h": "xlib", - "X11/extensions/Xrandr.h": "xlib_xrandr", - "wayland-client.h": "wayland", - "windows.h": "win32", - "xcb/xcb.h": "xcb", - "directfb.h": "directfb", - "zircon/types.h": "fuchsia", - "ggp_c/vulkan_types.h": "ggp", - "screen/screen.h": "screen", - "nvscisync.h": "nvidia", - "nvscibuf.h": "nvidia", - "vk_video/vulkan_video_codec_h264std.h": "vk_video", - "vk_video/vulkan_video_codec_h264std_decode.h": "vk_video", - "vk_video/vulkan_video_codec_h264std_encode.h": "vk_video", - "vk_video/vulkan_video_codec_h265std.h": "vk_video", - "vk_video/vulkan_video_codec_h265std_decode.h": "vk_video", - "vk_video/vulkan_video_codec_h265std_encode.h": "vk_video", + "X11/Xlib.h": @["xlib", "xlib_xrandr"], + "X11/extensions/Xrandr.h": @["xlib_xrandr"], + "wayland-client.h": @["wayland"], + "windows.h": @["win32"], + "xcb/xcb.h": @["xcb"], + "directfb.h": @["directfb"], + "zircon/types.h": @["fuchsia"], + "ggp_c/vulkan_types.h": @["ggp"], + "screen/screen.h": @["screen"], + "nvscisync.h": @["sci"], + "nvscibuf.h": @["sci"], + "vk_video/vulkan_video_codec_h264std.h": @["provisional"], + "vk_video/vulkan_video_codec_h264std_decode.h": @["provisional"], + "vk_video/vulkan_video_codec_h264std_encode.h": @["provisional"], + "vk_video/vulkan_video_codec_h265std.h": @["provisional"], + "vk_video/vulkan_video_codec_h265std_decode.h": @["provisional"], + "vk_video/vulkan_video_codec_h265std_encode.h": @["provisional"], }.toTable MAP_KEYWORD = { "object": "theobject", "type": "thetype", }.toTable + SPECIAL_DEPENDENCIES = { + "VK_NV_ray_tracing": "VK_KHR_ray_tracing_pipeline", + }.toTable # helpers func mapType(typename: string): auto = @@ -159,9 +162,10 @@ # generate bitsets (normal enums in the C API, but bitfield-enums in Nim) elif node.attr("type") == "bitmask": for value in node.findAll("enum"): - if value.hasAttr("alias") or not value.hasAttr("bitpos"): - continue - values[smartParseInt(value.attr("bitpos"))] = value.attr("name") + if value.hasAttr("bitpos"): + values[smartParseInt(value.attr("bitpos"))] = value.attr("name") + elif node.attr("name") == "VkVideoEncodeRateControlModeFlagBitsKHR": # special exception, for some reason this has values instead of bitpos + values[smartParseInt(value.attr("value"))] = value.attr("name") if values.len > 0: if node.hasAttr("bitwidth"): result.add " " & name & "* {.size: " & $(smartParseInt(node.attr("bitwidth")) div 8) & ".} = enum" @@ -193,8 +197,8 @@ let name = node.attr("name") var union = "" if node.attr("category") == "union": - union = "{.union.}" - result.add &" {name}* {union} = object" + union = "{.union.} " + result.add &" {name}* {union}= object" for member in node.findAll("member"): if not member.hasAttr("api") or member.attr("api") == "vulkan": let fieldname = member.child("name")[0].text.strip(chars={'_'}) @@ -219,9 +223,9 @@ paramname = mapName(paramname) params.add &"{paramname}: {mapType(paramtype)}" let paramsstr = params.join(", ") - result.add(&" {name} = proc({paramsstr}): {returntype} {{.cdecl.}}") + result.add(&" {name}* = proc({paramsstr}): {returntype} {{.cdecl.}}") -func serializeType(node: XmlNode): Table[string, seq[string]] = +func serializeType(node: XmlNode, headerTypes: Table[string, string]): Table[string, seq[string]] = if node.attrsLen == 0: return if node.attr("requires") == "vk_platform" or node.attr("category") == "include": @@ -230,12 +234,12 @@ result["enums"] = @[] # include-defined types (in platform headers) - if node.hasAttr("requires") and node.hasAttr("name") and node.attr("category") != "define": - let platform = "platform/" & PLATFORM_HEADER_MAP[node.attr("requires")] - if not result.hasKey(platform): - result[platform] = @[] - result[platform].add "type " & node.attr( - "name") & " {.header: \"" & node.attr("requires") & "\".} = object" + if node.attr("name") in headerTypes: + for platform in PLATFORM_HEADER_MAP[node.attr("requires")]: + let platformfile = "platform/" & platform + if not result.hasKey(platformfile): + result[platformfile] = @[] + 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") @@ -274,6 +278,23 @@ else: discard +func serializeCommand(node: XmlNode): (string, string) = + let + proto = node.child("proto") + resulttype = mapType(proto.child("type")[0].text) + name = proto.child("name")[0].text + var params: seq[string] + for param in node: + if param.tag == "param" and param.attr("api") in ["", "vulkan"]: + let fieldname = param.child("name")[0].text.strip(chars={'_'}) + var fieldtype = param.child("type")[0].text.strip(chars={'_'}) + if param[param.len - 2].kind == xnText and param[param.len - 2].text.strip() == "*": + fieldtype = &"ptr {mapType(fieldtype)}" + fieldtype = mapType(fieldtype) + params.add &"{mapName(fieldname)}: {fieldtype}" + let allparams = params.join(", ") + return (name, &"proc({allparams}): {resulttype} {{.stdcall.}}") + proc update(a: var Table[string, seq[string]], b: Table[string, seq[string]]) = for k, v in b.pairs: @@ -305,14 +326,27 @@ platformTypes[command.attr("name")] = extension.attr("platform") elif extension.attr("name").startsWith("VK_KHR_video"): for thetype in extension.findAll("type"): - platformTypes[thetype.attr("name")] = "vk_video" + platformTypes[thetype.attr("name")] = "provisional" for command in extension.findAll("command"): - platformTypes[command.attr("name")] = "vk_video" + platformTypes[command.attr("name")] = "provisional" var outputFiles = { - "basetypes": @["type", " VkHandle* = distinct pointer", " VkNonDispatchableHandle* = distinct pointer"], - "structs": @["import ./enums", "import ./basetypes", "type"], - "enums": @["import ./basetypes", "type"], + "basetypes": @[ + "import std/dynlib", + "type", + " VkHandle* = distinct pointer", + " VkNonDispatchableHandle* = distinct pointer", + "when defined(linux):", + " let vulkanLib* = loadLib(\"libvulkan.so.1\")", + "when defined(windows):", + " let vulkanLib* = loadLib(\"vulkan-1.dll\")", + "if vulkanLib == nil:", + " raise newException(Exception, \"Unable to load vulkan library\")", + "type", + ], + "structs": @["type"], + "enums": @["type"], + "commands": @[], }.toTable # enums @@ -332,15 +366,141 @@ outfile = "platform/" & platformTypes[thetype.attr("name")] if not (outfile in outputFiles): outputFiles[outfile] = @[] - outputFiles[outfile].add "type" outputFiles[outfile].add serializeStruct(thetype, api) # types + var headerTypes: Table[string, string] + for types in api.findAll("types"): + for thetype in types.findAll("type"): + if thetype.attrsLen == 2 and thetype.hasAttr("requires") and thetype.hasAttr("name") and thetype.attr("requires") != "vk_platform": + let name = thetype.attr("name") + let incld = thetype.attr("requires") + headerTypes[name] = &"{name} {{.header: \"{incld}\".}} = object" + for typesgroup in api.findAll("types"): for thetype in typesgroup.findAll("type"): - outputFiles.update serializeType(thetype) + outputFiles.update serializeType(thetype, headerTypes) + + # commands aka functions + var varDecls: Table[string, string] + 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.hasAttr("alias"): + let name = command.attr("name") + let alias = command.attr("alias") + let thetype = varDecls[alias].split(":", 1)[1].strip() + varDecls[name] = &" {name}*: {thetype}" + procLoads[name] = &" {name} = {alias}" + else: + let (name, thetype) = serializeCommand(command) + varDecls[name] = &" {name}*: {thetype}" + procLoads[name] = &" {name} = cast[{thetype}](checkedSymAddr(vulkanLib, \"{name}\"))" + var declared: seq[string] + var featureloads: seq[string] + for feature in api.findAll("feature"): + if feature.attr("api") in ["vulkan", "vulkan,vulkansc"]: + let name = feature.attr("name") + outputFiles["commands"].add &"# feature {name}" + outputFiles["commands"].add "var" + for command in feature.findAll("command"): + if not (command.attr("name") in declared): + outputFiles["commands"].add varDecls[command.attr("name")] + declared.add command.attr("name") + featureloads.add &"load{name}" + outputFiles["commands"].add &"proc load{name}*() =" + for command in feature.findAll("command"): + outputFiles["commands"].add procLoads[command.attr("name")] + outputFiles["commands"].add "" + outputFiles["commands"].add ["proc loadAll*() ="] + for l in featureloads: + outputFiles["commands"].add [&" {l}()"] + outputFiles["commands"].add "" + + # for promoted extensions, dependants need to call the load-function of the promoted feature/extension + # use table to store promotions + var promotions: Table[string, string] + for extensions in api.findAll("extensions"): + for extension in extensions.findAll("extension"): + if extension.hasAttr("promotedto"): + promotions[extension.attr("name")] = extension.attr("promotedto") + + var extensionDependencies: Table[string, (seq[string], XmlNode)] + var features: seq[string] + for feature in api.findAll("feature"): + features.add feature.attr("name") + for extensions in api.findAll("extensions"): + for extension in extensions.findAll("extension"): + let name = extension.attr("name") + extensionDependencies[name] = (@[], extension) + if extension.hasAttr("depends"): + extensionDependencies[name] = (extension.attr("depends").split("+"), extension) + if extension.attr("depends").startsWith("("): # no need for full tree parser, only single place where we can use a feature + let dependencies = extension.attr("depends").rsplit({')'}, 1)[1][1 .. ^1].split("+") + extensionDependencies[name] = (dependencies, extension) + if name in SPECIAL_DEPENDENCIES: + extensionDependencies[name][0].add SPECIAL_DEPENDENCIES[name] + + var dependencyOrderedExtensions: OrderedTable[string, XmlNode] + while extensionDependencies.len > 0: + var delkeys: seq[string] + for extensionName, (dependencies, extension) in extensionDependencies.pairs: + var missingExtension = false + for dep in dependencies: + let realdep = promotions.getOrDefault(dep, dep) + if not (realdep in dependencyOrderedExtensions) and not (realdep in features): + missingExtension = true + break + if not missingExtension: + dependencyOrderedExtensions[extensionName] = extension + delkeys.add extensionName + for key in delkeys: + extensionDependencies.del key + + 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" + 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): + outputFiles[file].add varDecls[command.attr("name")] + declared.add command.attr("name") + outputFiles[file].add &"proc 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)}()" + addedFunctionBody = true + for command in extension.findAll("command"): + outputFiles[file].add procLoads[command.attr("name")] + addedFunctionBody = true + if not addedFunctionBody: + outputFiles[file].add " discard" + outputFiles[file].add "" + + var mainout: seq[string] + for section in ["basetypes", "enums", "structs", "commands"]: + mainout.add outputFiles[section] + writeFile outdir / &"types.nim", mainout.join("\n") + for filename, filecontent in outputFiles.pairs: - writeFile outdir / &"{filename}.nim", filecontent.join("\n") + if filename.startsWith("platform/"): + writeFile outdir / &"{filename}.nim", (@[ + "import std/dynlib", + "import ../types", + "type" + ] & filecontent).join("\n") when isMainModule: main()