comparison src/vulkan_api/vulkan_api_generator.nim @ 545:39aed3128be7

fix: tons of errors in wrapper generator, can now compile, extension function not loaded yet it seems
author Sam <sam@basx.dev>
date Fri, 24 Feb 2023 01:32:45 +0700
parents c3c772512e7c
children e872cf354110
comparison
equal deleted inserted replaced
544:c3c772512e7c 545:39aed3128be7
81 parseInt(value) 81 parseInt(value)
82 func hasAttr(node: XmlNode, attr: string): bool = node.attr(attr) != "" 82 func hasAttr(node: XmlNode, attr: string): bool = node.attr(attr) != ""
83 func tableSorted(table: Table[int, string]): seq[(int, string)] = 83 func tableSorted(table: Table[int, string]): seq[(int, string)] =
84 result = toSeq(table.pairs) 84 result = toSeq(table.pairs)
85 result.sort((a, b) => cmp(a[0], b[0])) 85 result.sort((a, b) => cmp(a[0], b[0]))
86 func findType(declNode: XmlNode): string =
87 # examples:
88 # char** -> cstringArray
89 # void* -> pointer
90 # char* -> cstring
91 #
92 # int* -> ptr int
93 # void** -> ptr pointer
94 # int** -> ptr ptr int
95 var basetype = ""
96 var apointer = ""
97 var arraylen = ""
98 for child in declNode:
99 if child.kind == xnText:
100 if "[" in child.text:
101 if "[" in child.text and "]" in child.text:
102 arraylen = child.text.strip(chars={'[', ']'}).replace("][", "*")
103 else:
104 arraylen = declNode.child("enum")[0].text
105 else:
106 for i in 0 ..< child.text.count('*'):
107 apointer = apointer & "ptr "
108 elif child.tag == "type":
109 basetype = mapType(child[0].text)
110 if basetype == "void":
111 if apointer.count("ptr ") > 0:
112 basetype = "pointer"
113 apointer = apointer[0 ..< ^4]
114 elif basetype == "char":
115 if apointer.count("ptr ") == 1:
116 basetype = "cstring"
117 apointer = ""
118 elif apointer.count("ptr ") == 2:
119 basetype = "cstringArray"
120 apointer = ""
121 elif apointer.count("ptr ") > 2:
122 basetype = "cstringArray"
123 apointer = apointer[0 ..< ^8]
124
125 result = &"{apointer}{basetype}"
126 if arraylen != "":
127 result = &"array[{arraylen}, {result}]"
86 128
87 # serializers 129 # serializers
88 func serializeEnum(node: XmlNode, api: XmlNode): seq[string] = 130 func serializeEnum(node: XmlNode, api: XmlNode): seq[string] =
89 let name = node.attr("name") 131 let name = node.attr("name")
90 if name == "": 132 if name == "":
172 let enumEntry = &" {thename} = {value}" 214 let enumEntry = &" {thename} = {value}"
173 result.add enumEntry 215 result.add enumEntry
174 216
175 # generate bitsets (normal enums in the C API, but bitfield-enums in Nim) 217 # generate bitsets (normal enums in the C API, but bitfield-enums in Nim)
176 elif node.attr("type") == "bitmask": 218 elif node.attr("type") == "bitmask":
219 var predefined_enum_sets: seq[string]
177 for value in node.findAll("enum"): 220 for value in node.findAll("enum"):
178 if value.hasAttr("bitpos"): 221 if value.hasAttr("bitpos"):
179 values[smartParseInt(value.attr("bitpos"))] = value.attr("name") 222 values[smartParseInt(value.attr("bitpos"))] = value.attr("name")
180 elif node.attr("name") == "VkVideoEncodeRateControlModeFlagBitsKHR": # special exception, for some reason this has values instead of bitpos 223 elif node.attr("name") == "VkVideoEncodeRateControlModeFlagBitsKHR": # special exception, for some reason this has values instead of bitpos
181 values[smartParseInt(value.attr("value"))] = value.attr("name") 224 values[smartParseInt(value.attr("value"))] = value.attr("name")
225 elif value.hasAttr("value"): # create a const that has multiple bits set
226 predefined_enum_sets.add &" {value.attr(\"name\")}* = {value.attr(\"value\")}"
227
182 if values.len > 0: 228 if values.len > 0:
183 if node.hasAttr("bitwidth"): 229 if node.hasAttr("bitwidth"):
184 result.add " " & name & "* {.size: " & $(smartParseInt(node.attr("bitwidth")) div 8) & ".} = enum" 230 result.add " " & name & "* {.size: 8.} = enum"
185 else: 231 else:
186 result.add " " & name & "* {.size: sizeof(cint).} = enum" 232 result.add " " & name & "* {.size: sizeof(cint).} = enum"
187 for (bitpos, enumvalue) in tableSorted(values): 233 for (bitpos, enumvalue) in tableSorted(values):
188 var value = "00000000000000000000000000000000"# makes the bit mask nicely visible 234 var value = "00000000000000000000000000000000"# makes the bit mask nicely visible
189 if node.hasAttr("bitwidth"): # assumes this is always 64 235 if node.hasAttr("bitwidth"): # assumes this is always 64
190 value = value & value 236 value = value & value
191 value[^(bitpos + 1)] = '1' 237 value[^(bitpos + 1)] = '1'
192 let enumEntry = &" {enumvalue} = 0b{value}" 238 let enumEntry = &" {enumvalue} = 0b{value}"
193 if not (enumEntry in result): # the specs define duplicate entries for backwards compat 239 if not (enumEntry in result): # the specs define duplicate entries for backwards compat
194 result.add enumEntry 240 result.add enumEntry
195 let cApiName = name.replace("FlagBits", "Flags") 241 let cApiName = name.replace("FlagBits", "Flags")
196 if node.hasAttr("bitwidth"): # assumes this is always 64 242 if node.hasAttr("bitwidth"): # assuming this attribute is always 64
197 if values.len > 0: 243 if values.len > 0:
198 result.add &"""converter BitsetToNumber*(flags: openArray[{name}]): {cApiName} = 244 result.add &"""converter BitsetToNumber*(flags: openArray[{name}]): {cApiName} =
199 for flag in flags: 245 for flag in flags:
200 result = {cApiName}(uint64(result) or uint(flag))""" 246 result = {cApiName}(int64(result) or int64(flag))"""
201 result.add "type" 247 result.add &"""converter NumberToBitset*(number: {cApiName}): seq[{name}] =
202 else: 248 for value in {name}.items:
203 if values.len > 0: 249 if (value.ord and int64(number)) > 0:
204 result.add &"""converter BitsetToNumber*(flags: openArray[{name}]): {cApiName} = 250 result.add value"""
205 for flag in flags: 251 else:
206 result = {cApiName}(uint(result) or uint(flag))""" 252 if values.len > 0:
207 result.add "type" 253 result.add &"""func toBits*(flags: openArray[{name}]): {cApiName} =
254 for flag in flags:
255 result = {cApiName}(uint(result) or uint(flag))"""
256 result.add &"""func toEnums*(number: {cApiName}): seq[{name}] =
257 for value in {name}.items:
258 if (value.ord and cint(number)) > 0:
259 result.add value"""
260 if predefined_enum_sets.len > 0:
261 result.add "const"
262 result.add predefined_enum_sets
263 result.add "type"
264
208 265
209 func serializeStruct(node: XmlNode): seq[string] = 266 func serializeStruct(node: XmlNode): seq[string] =
210 let name = node.attr("name") 267 let name = node.attr("name")
211 var union = "" 268 var union = ""
212 if node.attr("category") == "union": 269 if node.attr("category") == "union":
213 union = "{.union.} " 270 union = "{.union.} "
214 result.add &" {name}* {union}= object" 271 result.add &" {name}* {union}= object"
215 for member in node.findAll("member"): 272 for member in node.findAll("member"):
216 if not member.hasAttr("api") or member.attr("api") == "vulkan": 273 if not member.hasAttr("api") or member.attr("api") == "vulkan":
217 let fieldname = member.child("name")[0].text.strip(chars={'_'}) 274 let fieldname = member.child("name")[0].text.strip(chars={'_'})
218 var fieldtype = member.child("type")[0].text.strip(chars={'_'}) 275 result.add &" {mapName(fieldname)}*: {findType(member)}"
219 # detect pointers
220 for child in member:
221 if child.kind == xnText and child.text.strip() == "*":
222 fieldtype = &"ptr {mapType(fieldtype)}"
223 elif child.kind == xnText and child.text.strip() == "* const*":
224 fieldtype = "cstringArray"
225 fieldtype = mapType(fieldtype)
226 # detect arrays
227 for child in member:
228 if child.kind == xnText and child.text.endsWith("]"):
229 var thelen = ""
230 if "[" in child.text:
231 thelen = child.text.strip(chars={'[', ']'}).replace("][", "*")
232 else:
233 thelen = member.child("enum")[0].text
234 fieldtype = &"array[{thelen}, {fieldtype}]"
235 result.add &" {mapName(fieldname)}*: {fieldtype}"
236 276
237 func serializeFunctiontypes(api: XmlNode): seq[string] = 277 func serializeFunctiontypes(api: XmlNode): seq[string] =
238 for node in api.findAll("type"): 278 for node in api.findAll("type"):
239 if node.attr("category") == "funcpointer": 279 if node.attr("category") == "funcpointer":
240 let name = node[1][0] 280 let name = node[1][0]
328 name = proto.child("name")[0].text 368 name = proto.child("name")[0].text
329 var params: seq[string] 369 var params: seq[string]
330 for param in node: 370 for param in node:
331 if param.tag == "param" and param.attr("api") in ["", "vulkan"]: 371 if param.tag == "param" and param.attr("api") in ["", "vulkan"]:
332 let fieldname = param.child("name")[0].text.strip(chars={'_'}) 372 let fieldname = param.child("name")[0].text.strip(chars={'_'})
333 var fieldtype = param.child("type")[0].text.strip(chars={'_'}) 373 params.add &"{mapName(fieldname)}: {findType(param)}"
334 if param[param.len - 2].kind == xnText and param[param.len - 2].text.strip() == "*":
335 fieldtype = &"ptr {mapType(fieldtype)}"
336 fieldtype = mapType(fieldtype)
337 params.add &"{mapName(fieldname)}: {fieldtype}"
338 let allparams = params.join(", ") 374 let allparams = params.join(", ")
339 return (name, &"proc({allparams}): {resulttype} {{.stdcall.}}") 375 return (name, &"proc({allparams}): {resulttype} {{.stdcall.}}")
340 376
341 377
342 proc update(a: var Table[string, seq[string]], b: Table[string, seq[string]]) = 378 proc update(a: var Table[string, seq[string]], b: Table[string, seq[string]]) =
376 412
377 var outputFiles = { 413 var outputFiles = {
378 "basetypes": @[ 414 "basetypes": @[
379 "import std/dynlib", 415 "import std/dynlib",
380 "import std/tables", 416 "import std/tables",
417 "import std/strutils",
418 "import std/logging",
419 "import std/macros",
420 "import std/private/digitsutils",
421 "from typetraits import HoleyEnum",
381 "type", 422 "type",
382 " VkHandle* = distinct uint", 423 " VkHandle* = distinct uint",
383 " VkNonDispatchableHandle* = distinct uint", 424 " VkNonDispatchableHandle* = distinct uint",
384 "when defined(linux):", 425 "when defined(linux):",
385 " let vulkanLib* = loadLib(\"libvulkan.so.1\")", 426 " let vulkanLib* = loadLib(\"libvulkan.so.1\")",
402 let value = call 443 let value = call
403 if value != VK_SUCCESS: 444 if value != VK_SUCCESS:
404 error "Vulkan error: ", astToStr(call), " returned ", $value 445 error "Vulkan error: ", astToStr(call), " returned ", $value
405 raise newException(Exception, "Vulkan error: " & astToStr(call) & 446 raise newException(Exception, "Vulkan error: " & astToStr(call) &
406 " returned " & $value)""", 447 " returned " & $value)""",
448 """
449 # custom enum iteration (for enum values > 2^16)
450 macro enumFullRange(a: typed): untyped =
451 newNimNode(nnkBracket).add(a.getType[1][1..^1])
452
453 iterator items[T: HoleyEnum](E: typedesc[T]): T =
454 for a in enumFullRange(E): yield a""",
407 ], 455 ],
408 "structs": @["type"], 456 "structs": @["type"],
409 "enums": @["type"], 457 "enums": @["type"],
410 "commands": @[], 458 "commands": @[],
411 }.toTable 459 }.toTable
581 mainout.add "# load global functions immediately" 629 mainout.add "# load global functions immediately"
582 mainout.add "block globalFunctions:" 630 mainout.add "block globalFunctions:"
583 mainout.add " let instance = VkInstance(0)" 631 mainout.add " let instance = VkInstance(0)"
584 for l in GLOBAL_COMMANDS: 632 for l in GLOBAL_COMMANDS:
585 mainout.add procLoads[l] 633 mainout.add procLoads[l]
586 writeFile outdir / &"api.nim", mainout.join("\n")
587
588 mainout.add "" 634 mainout.add ""
589 mainout.add "converter VkBool2NimBool*(a: VkBool32): bool = a > 0" 635 mainout.add "converter VkBool2NimBool*(a: VkBool32): bool = a > 0"
590 mainout.add "converter NimBool2VkBool*(a: bool): VkBool32 = VkBool32(a)" 636 mainout.add "converter NimBool2VkBool*(a: bool): VkBool32 = VkBool32(a)"
637 mainout.add "proc `$`*(x: uint32): string {.raises: [].} = addInt(result, x)"
638
639 writeFile outdir / &"api.nim", mainout.join("\n")
640
591 641
592 for filename, filecontent in outputFiles.pairs: 642 for filename, filecontent in outputFiles.pairs:
593 if filename.startsWith("platform/"): 643 if filename.startsWith("platform/"):
594 writeFile outdir / &"{filename}.nim", (@[ 644 writeFile outdir / &"{filename}.nim", (@[
595 "type" 645 "type"