Mercurial > games > semicongine
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" |