Mercurial > games > semicongine
annotate svk/generate.nim @ 1482:bca8f65ed4ed
did: continue on generator
| author | sam <sam@basx.dev> |
|---|---|
| date | Mon, 28 Apr 2025 01:22:07 +0700 |
| parents | a99f3227130c |
| children | 55911f736a5a |
| rev | line source |
|---|---|
| 1479 | 1 import std/strtabs |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
2 import std/syncio |
| 1479 | 3 import std/xmltree |
| 4 import std/tables | |
| 5 import std/options | |
| 6 import std/sequtils | |
| 7 import std/strutils | |
| 8 import std/strformat | |
| 9 import std/xmlparser | |
| 10 import std/os | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
11 import std/osproc |
| 1479 | 12 import std/paths |
| 13 | |
| 14 # helpers | |
| 15 func smartParseInt(value: string): int = | |
| 16 if value.startsWith("0x"): | |
| 17 parseHexInt(value) | |
| 18 else: | |
| 19 parseInt(value) | |
| 20 | |
| 21 const TYPEMAP = { | |
| 22 "void": "void", | |
| 23 "char": "char", | |
| 24 "float": "float32", | |
| 25 "double": "float64", | |
| 26 "int8_t": "int8", | |
| 27 "uint8_t": "uint8", | |
| 28 "int16_t": "int16", | |
| 29 "uint16_t": "uint16", | |
| 30 "int32_t": "int32", | |
| 31 "uint32_t": "uint32", | |
| 32 "uint64_t": "uint64", | |
| 33 "int64_t": "int64", | |
| 34 "size_t": "csize_t", | |
| 35 "int": "cint", | |
| 36 }.toTable | |
| 37 # load xml | |
| 38 let xml = (system.currentSourcePath.parentDir() / "vk.xml").loadXml() | |
| 39 let platforms = xml.findAll("platforms")[0] | |
| 40 let types = xml.findAll("types")[0] | |
| 41 let xmlenums = xml.findAll("enums") | |
| 42 let commands = xml.findAll("commands")[0] | |
| 43 let features = xml.findAll("feature") # features has extends="<ENUM>" | |
| 44 let extensions = xml.findAll("extensions")[0] # extensions has extends="<ENUM>" | |
| 45 let formats = xml.findAll("formats")[0] | |
| 46 | |
| 47 # gather all enums | |
| 48 | |
| 49 type | |
| 50 EnumEntry = object | |
| 51 name: string | |
| 52 value: string | |
| 53 | |
| 54 EnumDef = object | |
| 55 name: string | |
| 56 values: seq[EnumEntry] | |
| 57 isBitmask: bool | |
| 58 | |
| 59 ConstantDef = object | |
| 60 name: string | |
| 61 datatype: string | |
| 62 value: string | |
| 63 | |
| 64 var consts: seq[ConstantDef] | |
| 65 var enums: Table[string, EnumDef] | |
| 66 | |
| 67 func addValue(edef: var EnumDef, n: XmlNode) = | |
| 68 if n.attr("deprecated") != "aliased" and n.attr("alias") == "": | |
| 69 if n.attr("name") in edef.values.mapIt(it.name): | |
| 70 return | |
| 71 | |
| 72 var value = "" | |
| 73 if n.attr("value") != "": | |
| 74 value = n.attr("value") | |
| 75 elif n.attr("bitpos") != "": | |
| 76 value = $(1 shl parseInt(n.attr("bitpos"))) | |
| 77 elif n.attr("offset") != "": | |
| 78 var enumBase = 1000000000 | |
| 79 if n.attr("extnumber") != "": | |
| 80 enumBase += (smartParseInt(n.attr("extnumber")) - 1) * 1000 | |
| 81 var v = smartParseInt(n.attr("offset")) + enumBase | |
| 82 if n.attr("dir") == "-": | |
| 83 v = -v | |
| 84 value = $(v) | |
| 85 | |
| 1482 | 86 if value notin edef.values.mapIt(it.value): |
| 87 edef.values.add EnumEntry(name: n.attr("name"), value: value) | |
| 1479 | 88 |
| 1481 | 89 func doTypename(typename: string, isPointer: bool): string = |
| 90 result = TYPEMAP.getOrDefault(typename.strip(), typename.strip()).strip(chars = {'_'}) | |
| 91 | |
| 92 if typename == "void": | |
| 93 assert isPointer | |
| 94 | |
| 95 if isPointer: | |
| 96 if typename == "void": | |
| 97 result = "pointer" | |
| 98 elif typename == "char": | |
| 99 result = "cstring" | |
| 100 else: | |
| 101 result = "ptr " & result | |
| 102 | |
| 103 func doIdentifier(typename: string): string = | |
| 104 if typename in ["type", "object"]: | |
| 105 return &"`{typename}`" | |
| 106 return typename.strip() | |
| 107 | |
| 108 func doMember(typename, theType: string, isPointer: bool, value: string): string = | |
| 109 if value == "": | |
| 110 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)}" | |
| 111 else: | |
| 112 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)} = {value}" | |
| 113 | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
114 func memberDecl(n: XmlNode): string = |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
115 for i in 0 ..< n.len: |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
116 if n[i].kind == xnElement and n[i].tag == "comment": |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
117 n.delete(i) |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
118 break |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
119 assert n.tag == "member" |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
120 if n.len == 2: |
| 1481 | 121 return doMember(n[1][0].text, n[0][0].text, false, n.attr("values")) |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
122 elif n.len == 3: |
| 1481 | 123 assert "*" notin n[0][0].text.strip() |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
124 if n[1].kind == xnElement and n[1].tag == "name": |
| 1481 | 125 # bitfield |
| 126 if n[2].text.strip().startsWith(":"): | |
| 127 return | |
| 128 &"{doIdentifier(n[1][0].text)} {{.bitsize:{n[2].text.strip()[1 .. ^1]}.}}: {doTypename(n[0][0].text, false)}" | |
| 129 # array definition | |
| 130 elif n[2].text.strip().startsWith("["): | |
| 131 let arrayDim = n[2].text[1 ..< ^1] | |
| 132 if "][" in arrayDim: | |
| 133 let dims = arrayDim.split("][", 1) | |
| 134 let (dim1, dim2) = (dims[0], dims[1]) | |
| 135 return doMember( | |
| 136 n[1][0].text, | |
| 137 &"array[{dim1}, array[{dim2}, {doTypename(n[0][0].text, false)}]]", | |
| 138 false, | |
| 139 n.attr("values"), | |
| 140 ) | |
| 141 else: | |
| 142 return doMember( | |
| 143 n[1][0].text, | |
| 144 &"array[{arrayDim}, {doTypename(n[0][0].text, false)}]", | |
| 145 false, | |
| 146 n.attr("values"), | |
| 147 ) | |
| 148 else: | |
| 149 debugecho n.toSeq | |
| 150 doAssert false, "This should not happen" | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
151 else: |
| 1481 | 152 # pointer definition |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
153 assert n[1].text.strip() == "*" |
| 1481 | 154 return doMember(n[2][0].text, n[0][0].text, true, n.attr("values")) |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
155 elif n.len == 4: |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
156 if n[0].text.strip() in ["struct", "const struct"]: |
| 1481 | 157 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
158 else: |
| 1481 | 159 assert n[0].text.strip() == "const" # can be ignored |
| 160 assert n[1].tag == "type" | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
161 assert n[2].text.strip() in ["*", "* const *", "* const*"] |
| 1481 | 162 # can be ignored, basically every type is a pointer |
| 163 assert n[3].tag == "name" | |
| 164 assert n[1].len == 1 | |
| 165 assert n[3].len == 1 | |
| 166 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
167 elif n.len in [5, 6]: |
| 1481 | 168 # array definition, using const-value for array length |
| 169 # <type>uint8_t</type>,<name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>] | |
| 170 assert n[2].text.strip() == "[" | |
| 171 assert n[4].text.strip() == "]" | |
| 172 return doMember( | |
| 173 n[1][0].text, | |
| 174 &"array[{n[3][0].text}, {doTypename(n[0][0].text, false)}]", | |
| 175 false, | |
| 176 n.attr("values"), | |
| 177 ) | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
178 assert false |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
179 |
| 1479 | 180 for e in xmlenums: |
| 181 if e.attr("type") == "constants": | |
| 182 for c in e.findAll("enum"): | |
| 183 var value = c.attr("value").strip(chars = {'(', ')'}) | |
| 184 consts.add ConstantDef( | |
| 185 name: c.attr("name"), datatype: TYPEMAP[c.attr("type")], value: value | |
| 186 ) | |
| 187 elif e.attr("type") == "enum": | |
| 188 var edef = EnumDef(name: e.attr("name"), isBitmask: false) | |
| 189 for ee in e.findAll("enum"): | |
| 190 edef.addValue(ee) | |
| 191 enums[edef.name] = edef | |
| 192 elif e.attr("type") == "bitmask": | |
| 193 var edef = EnumDef(name: e.attr("name"), isBitmask: true) | |
| 194 for ee in e.findAll("enum"): | |
| 195 edef.addValue(ee) | |
| 196 enums[edef.name] = edef | |
| 197 | |
| 198 for f in features: | |
| 199 for extendenum in f.findAll("enum"): | |
| 200 if extendenum.attr("extends") != "": | |
| 201 enums[extendenum.attr("extends")].addValue(extendenum) | |
| 202 | |
| 203 for extension in extensions.findAll("extension"): | |
| 204 let extNum = extension.attr("number") | |
| 205 for extendenum in extension.findAll("enum"): | |
| 206 if extendenum.attr("extends") != "": | |
| 207 if extendenum.attr("extnumber") == "": | |
| 208 extendenum.attrs["extnumber"] = extNum | |
| 209 enums[extendenum.attr("extends")].addValue(extendenum) | |
| 210 | |
| 1481 | 211 let outPath = (system.currentSourcePath.parentDir() / "vkapi.nim") |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
212 let outFile = open(outPath, fmWrite) |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
213 |
| 1479 | 214 # generate core types =============================================================================== |
| 215 # preamble, much easier to hardcode than to generate from xml | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
216 outFile.writeLine """ |
| 1481 | 217 |
| 218 import ../semicongine/thirdparty/winim/winim/inc/winbase | |
| 219 import ../semicongine/thirdparty/winim/winim/inc/windef | |
| 220 import ../semicongine/thirdparty/x11/xlib | |
| 221 import ../semicongine/thirdparty/x11/x | |
| 222 import ../semicongine/thirdparty/x11/xrandr | |
| 223 | |
| 1479 | 224 func VK_MAKE_API_VERSION*( |
| 225 variant: uint32, major: uint32, minor: uint32, patch: uint32 | |
| 226 ): uint32 {.compileTime.} = | |
| 227 (variant shl 29) or (major shl 22) or (minor shl 12) or patch | |
| 228 """ | |
| 229 | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
230 outFile.writeLine "type" |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
231 outFile.writeLine """ |
| 1481 | 232 # some unused native types |
| 233 # | |
| 234 # android | |
| 235 ANativeWindow = object | |
| 236 AHardwareBuffer = object | |
| 237 | |
| 238 # apple | |
| 239 CAMetalLayer = object | |
| 240 MTLDevice = object | |
| 241 MTLCommandQueue = object | |
| 242 MTLBuffer = object | |
| 243 MTLTexture = object | |
| 244 MTLSharedEvent = object | |
| 245 MTLSharedEvent_id = object | |
| 246 | |
| 247 # wayland | |
| 248 wl_display = object | |
| 249 wl_surface = object | |
| 250 | |
| 251 # XCB | |
| 252 xcb_connection_t = object | |
| 253 xcb_window_t = object | |
| 254 xcb_visualid_t = object | |
| 255 | |
| 256 # directfb | |
| 257 IDirectFB = object | |
| 258 IDirectFBSurface = object | |
| 259 | |
| 260 # Zircon | |
| 261 zx_handle_t = object | |
| 262 | |
| 263 # GGP C | |
| 264 GgpStreamDescriptor = object | |
| 265 GgpFrameToken = object | |
| 266 | |
| 267 # Screen (nintendo switch?) | |
| 268 screen_context = object | |
| 269 screen_window = object | |
| 270 screen_buffer = object | |
| 271 | |
| 272 # Nvidia | |
| 273 NvSciSyncAttrList = object | |
| 274 NvSciSyncObj = object | |
| 275 NvSciSyncFence = object | |
| 276 NvSciBufAttrList = object | |
| 277 NvSciBufObj = object | |
| 278 | |
| 279 # some base vulkan base types | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
280 VkSampleMask = distinct uint32 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
281 VkBool32 = distinct uint32 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
282 VkFlags = distinct uint32 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
283 VkFlags64 = distinct uint64 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
284 VkDeviceSize = distinct uint64 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
285 VkDeviceAddress = distinct uint64 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
286 VkRemoteAddressNV = pointer |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
287 """ |
| 1479 | 288 |
| 289 # generate consts =============================================================================== | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
290 outFile.writeLine "const" |
| 1479 | 291 for c in consts: |
| 292 var value = c.value | |
| 293 if value.endsWith("U"): | |
| 294 value = value[0 ..^ 2] & "'u32" | |
| 295 elif value.endsWith("ULL"): | |
| 296 value = value[0 ..^ 4] & "'u64" | |
| 297 if value[0] == '~': | |
| 298 value = "not " & value[1 ..^ 1] | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
299 outFile.writeLine &" {c.name}*: {c.datatype} = {value}" |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
300 outFile.writeLine "" |
| 1479 | 301 |
| 302 # generate enums =============================================================================== | |
| 1481 | 303 const nameCollisions = [ |
| 304 "VK_PIPELINE_CACHE_HEADER_VERSION_ONE", | |
| 305 "VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE", | |
| 306 "VK_DEVICE_FAULT_VENDOR_BINARY_HEADER_VERSION_ONE_EXT", | |
| 307 ] | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
308 outFile.writeLine "type" |
| 1482 | 309 |
| 310 echo "#########################" | |
| 1479 | 311 for edef in enums.values(): |
| 1482 | 312 echo edef.values |
| 1479 | 313 if edef.values.len > 0: |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
314 outFile.writeLine &" {edef.name}* {{.size: 4.}} = enum" |
| 1479 | 315 for ee in edef.values: |
| 1481 | 316 # due to the nim identifier-system, there might be collisions between typenames and enum-member names |
| 317 if ee.name in nameCollisions: | |
| 318 outFile.writeLine &" {ee.name}_VALUE = {ee.value}" | |
| 319 else: | |
| 320 outFile.writeLine &" {ee.name} = {ee.value}" | |
| 321 | |
| 322 outFile.writeLine "" | |
| 323 | |
| 324 # generate types =============================================================================== | |
| 325 for t in types: | |
| 326 let category = t.attr("category") | |
| 327 if t.attr("api") == "vulkansc": | |
| 328 continue | |
| 329 elif t.attr("deprecated") == "true": | |
| 330 continue | |
| 331 elif category == "include": | |
| 332 continue | |
| 333 elif category == "define": | |
| 334 continue | |
| 335 elif t.attr("requires").startsWith("vk_video"): | |
| 336 continue | |
| 337 elif t.attr("alias") != "": | |
| 338 let a = t.attr("alias") | |
| 339 let n = t.attr("name") | |
| 340 outFile.writeLine &" {n} = {a}" | |
| 341 elif category == "bitmask": | |
| 342 if t.len > 0 and t[0].text.startsWith("typedef"): | |
| 343 outFile.writeLine &" {t[2][0].text} = distinct {t[1][0].text}" | |
| 344 elif category == "union": | |
| 345 let n = t.attr("name") | |
| 346 outFile.writeLine &" {n}* {{.union.}} = object" | |
| 347 for member in t.findAll("member"): | |
| 348 outFile.writeLine &" {member.memberDecl()}" | |
| 349 elif category == "handle": | |
| 350 outFile.writeLine &" {t[2][0].text} = distinct pointer" | |
| 351 elif category == "struct": | |
| 352 let n = t.attr("name") | |
| 353 outFile.writeLine &" {n}* = object" | |
| 354 for member in t.findAll("member"): | |
| 355 if member.attr("api") == "vulkansc": | |
| 356 continue | |
| 357 outFile.writeLine &" {member.memberDecl()}" | |
| 358 elif category == "funcpointer": | |
| 359 #[ | |
| 360 <type category="funcpointer">typedef void* (VKAPI_PTR *<name>PFN_vkAllocationFunction</name>)( | |
| 361 <type>void</type>* pUserData, | |
| 362 <type>size_t</type> size, | |
| 363 <type>size_t</type> alignment, | |
| 364 <type>VkSystemAllocationScope</type> allocationScope); | |
| 365 </type> | |
| 366 PFN_vkAllocationFunction* = proc( | |
| 367 pUserData: pointer, | |
| 368 size: csize_t, | |
| 369 alignment: csize_t, | |
| 370 allocationScope: VkSystemAllocationScope, | |
| 371 ): pointer {.cdecl.} | |
| 372 ]# | |
| 373 assert t[0].text.startsWith("typedef ") | |
| 374 outFile.writeLine &" {t[1][0].text}* = proc()" | |
| 375 else: | |
| 376 doAssert category in ["", "basetype", "enum"], "unknown type category: " & category | |
|
1480
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
377 outFile.writeLine "" |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
378 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
379 outFile.writeLine """ |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
380 when defined(linux): |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
381 include ../semicongine/rendering/vulkan/platform/xlib |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
382 when defined(windows): |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
383 include ../semicongine/rendering/vulkan/platform/win32 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
384 """ |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
385 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
386 outFile.close() |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
387 |
|
0b302531f5c5
did: continue on vulkan nim api generator
sam <sam@basx.dev>
parents:
1479
diff
changeset
|
388 assert execCmd("nim c " & outPath) == 0 |
