├── LICENSE ├── README.md ├── generator.nim ├── vulkanim.nimble └── vulkanim └── vulkan.nim /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Clyybber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkanim 2 | Vulkan bindings for nim 3 | 4 | # Features: 5 | * uses `distinct cint` instead of `enum` for the various vulkan constants, because nim's enums don't support duplicate values. 6 | * automatically converts FlagBit types to the appropriate Flags for convenience 7 | * includes a macro (called `vk`) to load vulkan API procedures at runtime 8 | -------------------------------------------------------------------------------- /generator.nim: -------------------------------------------------------------------------------- 1 | # Written by Leonardo Mariscal , 2019 2 | # Modified by Clyybber 3 | import strutils, httpClient, os, xmlparser, xmltree, streams, strformat, tables, algorithm, bitops 4 | 5 | # ## WARNING: This is a generated file. Do not edit 6 | # ## Any edits will be overwritten by the generator. 7 | # var vkGetProc: proc(procName: cstring): pointer {.cdecl.} 8 | # when not defined(vkCustomLoader): 9 | # import dynlib 10 | # when defined(windows): 11 | # const vkDLL = "vulkan-1.dll" 12 | # elif defined(macosx): 13 | # const vkDLL = "libMoltenVK.dylib" 14 | # else: 15 | # const vkDLL = "libvulkan.so.1" 16 | # let vkHandleDLL = loadLib(vkDLL) 17 | # if isNil(vkHandleDLL): 18 | # quit("could not load: " & vkDLL) 19 | # let vkGetProcAddress = cast[proc(s: cstring): pointer {.cdecl.}](symAddr(vkHandleDLL, "vkGetInstanceProcAddr")) 20 | # if vkGetProcAddress == nil: 21 | # quit("failed to load `vkGetInstanceProcAddr` from " & vkDLL) 22 | # vkGetProc = proc(procName: cstring): pointer {.cdecl.} = 23 | # when defined(windows): 24 | # result = vkGetProcAddress(procName) 25 | # if result != nil: 26 | # return 27 | # result = symAddr(vkHandleDLL, procName) 28 | # if result == nil: 29 | # raiseInvalidLibrary(procName) 30 | # proc setVKGetProc*(getProc: proc(procName: cstring): pointer {.cdecl.}) = 31 | # vkGetProc = getProc 32 | const srcHeader* = """ 33 | when defined(vkDynlib): 34 | const vulkandll = when defined(windows): 35 | "vulkan-1.dll" 36 | elif defined(macosx): 37 | "libMoltenVK.dylib" 38 | else: 39 | "libvulkan.so" 40 | {.pragma: load, dynlib: vulkandll.} 41 | else: 42 | {.pragma: load.} # User will link themselves 43 | 44 | type 45 | VkHandle* = int64 46 | VkNonDispatchableHandle* = int64 47 | """ 48 | 49 | # XXX: For manual dynamic mode 50 | # proc vkInit*(load1_0: bool = true, load1_1: bool = true): bool = 51 | # if load1_0: 52 | # vkLoad1_0() 53 | # when not defined(macosx): 54 | # if load1_1: 55 | # vkLoad1_1() 56 | # return true 57 | const vkInit* = """ 58 | import macros 59 | 60 | macro vk*(name, instance: untyped): untyped = 61 | nnkStmtList.newTree( 62 | nnkLetSection.newTree( 63 | nnkIdentDefs.newTree( 64 | ident "v" & name.strVal, 65 | ident "PFN" & name.strVal, 66 | nnkCast.newTree( 67 | ident "PFN" & name.strVal, 68 | nnkCall.newTree( 69 | ident "vkGetInstanceProcAddr", 70 | instance, 71 | newLit(name.strVal) 72 | ) ) ) ) ) 73 | """ 74 | 75 | let keywords* = ["addr", "and", "as", "asm", "bind", "block", "break", "case", "cast", "concept", 76 | "const", "continue", "converter", "defer", "discard", "distinct", "div", "do", 77 | "elif", "else", "end", "enum", "except", "export", "finally", "for", "from", "func", 78 | "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "let", 79 | "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", 80 | "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static", "template", 81 | "try", "tuple", "type", "using", "var", "when", "while", "xor", "yield"] 82 | 83 | const toEnums = false 84 | const dynamicMode = false 85 | 86 | type 87 | VkProc = object 88 | name: string 89 | rVal: string 90 | args: seq[VkArg] 91 | VkArg = object 92 | name: string 93 | argType: string 94 | VkStruct = object 95 | name: string 96 | members: seq[VkArg] 97 | 98 | var vkProcs: seq[VkProc] 99 | var vkStructs: seq[VkStruct] 100 | var vkStructureTypes: seq[string] 101 | 102 | proc snakeToCamel(s: string): string = 103 | result = s 104 | var lastWasUnderscore = true 105 | for i in (if result.startsWith("VK"): 2 elif result.startsWith("PFN"): 3 else: 0).. 0: 116 | result[0] = result[0].toLowerAscii() 117 | 118 | proc translateTypeAndDecl(s: string): string = 119 | s.replace("_screen_context", "screen_context") 120 | .replace("_screen_window", "screen_window") 121 | 122 | proc translateType(s: string): string = 123 | result = s 124 | .replace("int64_t", "int64") 125 | .replace("int32_t", "int32") 126 | .replace("int16_t", "int16") 127 | .replace("int8_t", "int8") 128 | .replace("size_t", "uint") # uint matches pointer size just like size_t 129 | .replace("float", "float32") 130 | .replace("double", "float64") 131 | .replace("VK_DEFINE_HANDLE", "VkHandle") 132 | .replace("VK_DEFINE_NON_DISPATCHABLE_HANDLE", "VkNonDispatchableHandle") 133 | .replace("const ", "") 134 | .replace(" const", "") 135 | .replace("unsigned ", "u") 136 | .replace("signed ", "") 137 | .replace("struct ", "") 138 | .translateTypeAndDecl() 139 | 140 | let levels = result.count('*') 141 | result = result.replace("*", "") 142 | for i in 0..= 32: strValue.add "'u64" 380 | elif e.attr("offset") != "": # Obtain values in the mapping from the attributes 381 | let offset = parseInt(e.attr("offset")) 382 | let extnumber = when not inExtension: 383 | if e.attr("extnumber") == "": continue # Otherwise we are actually in an extension 384 | parseInt(e.attr("extnumber")) 385 | else: parseInt(ext.attr("number")) 386 | # Now determine the actual enumerant value, as defined 387 | # in the "Layers and Extensions" appendix of the spec. 388 | numValue = extBase + (extnumber - 1) * extBlockSize + offset 389 | if e.attr("dir") == "-": numValue = -numvalue 390 | strValue = $numValue 391 | # More logic needed! 392 | elif e.attr("alias") != "": 393 | # TODO: generate aliases too 394 | discard #strValue = e.attr("alias") 395 | else: echo "fuck" 396 | 397 | if strValue != "": 398 | # if elements.hasKey(numValue): 399 | # dupes[numValue] = enumName 400 | # else: 401 | elements[numValue] = enumName 402 | 403 | for e in node.findAll("enum"): 404 | loopBody(false) 405 | for ext in node.findAll("extension"): 406 | for e in ext.findAll("enum"): 407 | loopBody(true) 408 | 409 | when toEnums: 410 | if not inType: 411 | output.add "\ntype\n" 412 | inType = true 413 | else: 414 | if enums.attr("bitwidth") != "": 415 | output.add "type {name}* = distinct uint{enums.attr(\"bitwidth\")}\n".fmt 416 | output.add dedent """ 417 | proc `==`*(x, y: {name}): bool {{.borrow.}} 418 | proc `-`*(x, y: {name}): {name} {{.borrow.}} 419 | proc `+`*(x, y: {name}): {name} {{.borrow.}} 420 | proc `+`*(x: {name}, y: int): {name} {{.used.}} = x + {name}(y) 421 | """.fmt 422 | else: 423 | output.add "type {name}* = distinct cint\n".fmt 424 | output.add dedent """ 425 | proc `==`*(x, y: {name}): bool {{.borrow.}} 426 | proc `-`*(x: {name}): {name} {{.borrow.}} 427 | proc `-`*(x, y: {name}): {name} {{.borrow.}} 428 | proc `+`*(x, y: {name}): {name} {{.borrow.}} 429 | proc `+`*(x: {name}, y: int): {name} {{.used.}} = x + {name}(y) 430 | """.fmt 431 | 432 | if "FlagBits" in name: 433 | let flagName = name.replace("Bits", "s") 434 | result.add dedent """ 435 | converter to{flagName}*(x: {name}): {flagName} = {flagName}(x) 436 | """.fmt 437 | 438 | if elements.len > 0: 439 | output.add "\nconst\n" 440 | inType = true 441 | 442 | if elements.len > 0: 443 | block dedup: 444 | var alreadyEncountered: Table[string, int] 445 | var dupes: seq[int] 446 | for k, v in (var copy = elements; copy): 447 | if alreadyEncountered.hasKey(v): 448 | discard #elements.del alreadyEncountered[v] # This modifies elements and crashes, even though we are iterating over a copy?? 449 | dupes.add alreadyEncountered[v] 450 | alreadyEncountered[v] = k 451 | for i in dupes: 452 | elements.del i 453 | 454 | when toEnums: 455 | output.add " {name}* {{.size: int32.sizeof.}} = enum\n".fmt 456 | elements.sort(system.cmp) 457 | for k, v in elements.pairs: 458 | var v = v#.constantCasing 459 | if name == "VkStructureType": 460 | vkStructureTypes.add v 461 | output.add when toEnums: 462 | " {v} = {k}\n".fmt 463 | else: 464 | " {v}* = {name} {k}\n".fmt 465 | 466 | if dupes.len > 0: 467 | for k, v in dupes: 468 | let v = v#.constantCasing 469 | output.add "const {v} = {k}\n".fmt 470 | echo k, v 471 | inType = false 472 | 473 | for ext in node.findAll("extension"): 474 | for e in ext.findAll("enum"): 475 | var strValue = "" 476 | var numValue = 0 477 | var enumName = e.attr("name") 478 | if e.attr("value") != "" and e.attr("extends") == "": 479 | strValue = e.attr("value") 480 | strValue = strValue.replace("%quot;","\"") 481 | #numValue = parseInt(strValue) # XXX: Check base, actually for all in this loop 482 | # If there's a non-integer, numeric "type" attribute (e.g. 'u' or 483 | # 'ull'), append it to the string value. 484 | # t = enuminfo.e.attr("type") 485 | # if t is not None and t != "" and t != "i" and t != "s": 486 | # strValue += enuminfo.type 487 | if strValue != "0": 488 | output.add "\nconst {enumName.constantCasing}* = {strValue}\n".fmt 489 | elif e.attr("bitpos") != "": 490 | let bitpos = parseInt(e.attr("bitpos")) 491 | numValue = 1 shl bitpos 492 | strValue = $numValue # XXX: Print as hex or binary 493 | if bitpos >= 32: strValue.add "'u64" #ULL 494 | elif e.attr("offset") != "": 495 | discard # Extending extension enums were already treated earlier 496 | elif e.attr("alias") != "": 497 | strValue = e.attr("alias") 498 | else: echo "fuck" 499 | 500 | proc genProcs(node: XmlNode, output: var string) = 501 | echo "Generating Procedures..." 502 | output.add "\n# Procs\n" & 503 | "type\n" 504 | for commands in node.findAll("commands"): 505 | for command in commands.findAll("command"): 506 | if command.child("proto") == nil: continue 507 | 508 | var vkProc = VkProc(name: command.child("proto").child("name").innerText, 509 | rVal: command.child("proto").innerText) 510 | vkProc.rVal = vkProc.rVal[0..= 1.0.0" 11 | --------------------------------------------------------------------------------