Mercurial > games > semicongine
comparison svk/generate.nim @ 1481:a99f3227130c
did: continue on vulkan-api generator
| author | sam <sam@basx.dev> |
|---|---|
| date | Sun, 27 Apr 2025 01:06:59 +0700 |
| parents | 0b302531f5c5 |
| children | bca8f65ed4ed |
comparison
equal
deleted
inserted
replaced
| 1480:0b302531f5c5 | 1481:a99f3227130c |
|---|---|
| 31 "uint32_t": "uint32", | 31 "uint32_t": "uint32", |
| 32 "uint64_t": "uint64", | 32 "uint64_t": "uint64", |
| 33 "int64_t": "int64", | 33 "int64_t": "int64", |
| 34 "size_t": "csize_t", | 34 "size_t": "csize_t", |
| 35 "int": "cint", | 35 "int": "cint", |
| 36 "void*": "pointer", | |
| 37 "char*": "cstring", | |
| 38 "ptr char": "cstring", | |
| 39 "ptr void": "pointer", | |
| 40 # "VK_DEFINE_HANDLE": "VkHandle", # not required, can directly defined as a distinct pointer, (in C is a pointer to an empty struct type) | |
| 41 # "VK_DEFINE_NON_DISPATCHABLE_HANDLE": "VkNonDispatchableHandle", # same here | |
| 42 }.toTable | 36 }.toTable |
| 43 | 37 |
| 44 # load xml | 38 # load xml |
| 45 let xml = (system.currentSourcePath.parentDir() / "vk.xml").loadXml() | 39 let xml = (system.currentSourcePath.parentDir() / "vk.xml").loadXml() |
| 46 let platforms = xml.findAll("platforms")[0] | 40 let platforms = xml.findAll("platforms")[0] |
| 93 v = -v | 87 v = -v |
| 94 value = $(v) | 88 value = $(v) |
| 95 | 89 |
| 96 edef.values.add EnumEntry(name: n.attr("name"), value: value) | 90 edef.values.add EnumEntry(name: n.attr("name"), value: value) |
| 97 | 91 |
| 92 func doTypename(typename: string, isPointer: bool): string = | |
| 93 result = TYPEMAP.getOrDefault(typename.strip(), typename.strip()).strip(chars = {'_'}) | |
| 94 | |
| 95 if typename == "void": | |
| 96 assert isPointer | |
| 97 | |
| 98 if isPointer: | |
| 99 if typename == "void": | |
| 100 result = "pointer" | |
| 101 elif typename == "char": | |
| 102 result = "cstring" | |
| 103 else: | |
| 104 result = "ptr " & result | |
| 105 | |
| 106 func doIdentifier(typename: string): string = | |
| 107 if typename in ["type", "object"]: | |
| 108 return &"`{typename}`" | |
| 109 return typename.strip() | |
| 110 | |
| 111 func doMember(typename, theType: string, isPointer: bool, value: string): string = | |
| 112 if value == "": | |
| 113 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)}" | |
| 114 else: | |
| 115 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)} = {value}" | |
| 116 | |
| 98 func memberDecl(n: XmlNode): string = | 117 func memberDecl(n: XmlNode): string = |
| 99 for i in 0 ..< n.len: | 118 for i in 0 ..< n.len: |
| 100 if n[i].kind == xnElement and n[i].tag == "comment": | 119 if n[i].kind == xnElement and n[i].tag == "comment": |
| 101 n.delete(i) | 120 n.delete(i) |
| 102 break | 121 break |
| 103 assert n.tag == "member" | 122 assert n.tag == "member" |
| 104 debugecho n.toSeq, " ", n.len | |
| 105 if n.len == 2: | 123 if n.len == 2: |
| 106 return &"{n[1][0].text}: {n[0][0]}" | 124 return doMember(n[1][0].text, n[0][0].text, false, n.attr("values")) |
| 107 elif n.len == 3: | 125 elif n.len == 3: |
| 126 assert "*" notin n[0][0].text.strip() | |
| 108 if n[1].kind == xnElement and n[1].tag == "name": | 127 if n[1].kind == xnElement and n[1].tag == "name": |
| 109 return | 128 # bitfield |
| 110 &"{n[1][0].text}: array[{n[2].text[1 ..< ^1]}, {TYPEMAP.getOrDefault(n[0][0].text, n[0][0].text)}]]" | 129 if n[2].text.strip().startsWith(":"): |
| 130 return | |
| 131 &"{doIdentifier(n[1][0].text)} {{.bitsize:{n[2].text.strip()[1 .. ^1]}.}}: {doTypename(n[0][0].text, false)}" | |
| 132 # array definition | |
| 133 elif n[2].text.strip().startsWith("["): | |
| 134 let arrayDim = n[2].text[1 ..< ^1] | |
| 135 if "][" in arrayDim: | |
| 136 let dims = arrayDim.split("][", 1) | |
| 137 let (dim1, dim2) = (dims[0], dims[1]) | |
| 138 return doMember( | |
| 139 n[1][0].text, | |
| 140 &"array[{dim1}, array[{dim2}, {doTypename(n[0][0].text, false)}]]", | |
| 141 false, | |
| 142 n.attr("values"), | |
| 143 ) | |
| 144 else: | |
| 145 return doMember( | |
| 146 n[1][0].text, | |
| 147 &"array[{arrayDim}, {doTypename(n[0][0].text, false)}]", | |
| 148 false, | |
| 149 n.attr("values"), | |
| 150 ) | |
| 151 else: | |
| 152 debugecho n.toSeq | |
| 153 doAssert false, "This should not happen" | |
| 111 else: | 154 else: |
| 155 # pointer definition | |
| 112 assert n[1].text.strip() == "*" | 156 assert n[1].text.strip() == "*" |
| 113 return &"{n[2][0].text}: ptr {n[0][0].text}" | 157 return doMember(n[2][0].text, n[0][0].text, true, n.attr("values")) |
| 114 elif n.len == 4: | 158 elif n.len == 4: |
| 115 if n[0].text.strip() in ["struct", "const struct"]: | 159 if n[0].text.strip() in ["struct", "const struct"]: |
| 116 return &"{n[3][0].text}: ptr {n[1][0].text}" | 160 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) |
| 117 else: | 161 else: |
| 162 assert n[0].text.strip() == "const" # can be ignored | |
| 163 assert n[1].tag == "type" | |
| 118 assert n[2].text.strip() in ["*", "* const *", "* const*"] | 164 assert n[2].text.strip() in ["*", "* const *", "* const*"] |
| 119 return &"?" | 165 # can be ignored, basically every type is a pointer |
| 166 assert n[3].tag == "name" | |
| 167 assert n[1].len == 1 | |
| 168 assert n[3].len == 1 | |
| 169 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) | |
| 120 elif n.len in [5, 6]: | 170 elif n.len in [5, 6]: |
| 121 return &"{n[1][0].text}: array[{n[3][0].text}, {n[0][0].text}]" | 171 # array definition, using const-value for array length |
| 172 # <type>uint8_t</type>,<name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>] | |
| 173 assert n[2].text.strip() == "[" | |
| 174 assert n[4].text.strip() == "]" | |
| 175 return doMember( | |
| 176 n[1][0].text, | |
| 177 &"array[{n[3][0].text}, {doTypename(n[0][0].text, false)}]", | |
| 178 false, | |
| 179 n.attr("values"), | |
| 180 ) | |
| 122 assert false | 181 assert false |
| 123 | 182 |
| 124 for e in xmlenums: | 183 for e in xmlenums: |
| 125 if e.attr("type") == "constants": | 184 if e.attr("type") == "constants": |
| 126 for c in e.findAll("enum"): | 185 for c in e.findAll("enum"): |
| 150 if extendenum.attr("extends") != "": | 209 if extendenum.attr("extends") != "": |
| 151 if extendenum.attr("extnumber") == "": | 210 if extendenum.attr("extnumber") == "": |
| 152 extendenum.attrs["extnumber"] = extNum | 211 extendenum.attrs["extnumber"] = extNum |
| 153 enums[extendenum.attr("extends")].addValue(extendenum) | 212 enums[extendenum.attr("extends")].addValue(extendenum) |
| 154 | 213 |
| 155 let outPath = (system.currentSourcePath.parentDir() / "api.nim") | 214 let outPath = (system.currentSourcePath.parentDir() / "vkapi.nim") |
| 156 let outFile = open(outPath, fmWrite) | 215 let outFile = open(outPath, fmWrite) |
| 157 | 216 |
| 158 # generate core types =============================================================================== | 217 # generate core types =============================================================================== |
| 159 # preamble, much easier to hardcode than to generate from xml | 218 # preamble, much easier to hardcode than to generate from xml |
| 160 outFile.writeLine """ | 219 outFile.writeLine """ |
| 220 | |
| 221 import ../semicongine/thirdparty/winim/winim/inc/winbase | |
| 222 import ../semicongine/thirdparty/winim/winim/inc/windef | |
| 223 import ../semicongine/thirdparty/x11/xlib | |
| 224 import ../semicongine/thirdparty/x11/x | |
| 225 import ../semicongine/thirdparty/x11/xrandr | |
| 226 | |
| 161 func VK_MAKE_API_VERSION*( | 227 func VK_MAKE_API_VERSION*( |
| 162 variant: uint32, major: uint32, minor: uint32, patch: uint32 | 228 variant: uint32, major: uint32, minor: uint32, patch: uint32 |
| 163 ): uint32 {.compileTime.} = | 229 ): uint32 {.compileTime.} = |
| 164 (variant shl 29) or (major shl 22) or (minor shl 12) or patch | 230 (variant shl 29) or (major shl 22) or (minor shl 12) or patch |
| 165 """ | 231 """ |
| 166 | 232 |
| 167 outFile.writeLine "type" | 233 outFile.writeLine "type" |
| 168 outFile.writeLine """ | 234 outFile.writeLine """ |
| 235 # some unused native types | |
| 236 # | |
| 237 # android | |
| 238 ANativeWindow = object | |
| 239 AHardwareBuffer = object | |
| 240 | |
| 241 # apple | |
| 242 CAMetalLayer = object | |
| 243 MTLDevice = object | |
| 244 MTLCommandQueue = object | |
| 245 MTLBuffer = object | |
| 246 MTLTexture = object | |
| 247 MTLSharedEvent = object | |
| 248 MTLSharedEvent_id = object | |
| 249 | |
| 250 # wayland | |
| 251 wl_display = object | |
| 252 wl_surface = object | |
| 253 | |
| 254 # XCB | |
| 255 xcb_connection_t = object | |
| 256 xcb_window_t = object | |
| 257 xcb_visualid_t = object | |
| 258 | |
| 259 # directfb | |
| 260 IDirectFB = object | |
| 261 IDirectFBSurface = object | |
| 262 | |
| 263 # Zircon | |
| 264 zx_handle_t = object | |
| 265 | |
| 266 # GGP C | |
| 267 GgpStreamDescriptor = object | |
| 268 GgpFrameToken = object | |
| 269 | |
| 270 # Screen (nintendo switch?) | |
| 271 screen_context = object | |
| 272 screen_window = object | |
| 273 screen_buffer = object | |
| 274 | |
| 275 # Nvidia | |
| 276 NvSciSyncAttrList = object | |
| 277 NvSciSyncObj = object | |
| 278 NvSciSyncFence = object | |
| 279 NvSciBufAttrList = object | |
| 280 NvSciBufObj = object | |
| 281 | |
| 282 # some base vulkan base types | |
| 169 VkSampleMask = distinct uint32 | 283 VkSampleMask = distinct uint32 |
| 170 VkBool32 = distinct uint32 | 284 VkBool32 = distinct uint32 |
| 171 VkFlags = distinct uint32 | 285 VkFlags = distinct uint32 |
| 172 VkFlags64 = distinct uint64 | 286 VkFlags64 = distinct uint64 |
| 173 VkDeviceSize = distinct uint64 | 287 VkDeviceSize = distinct uint64 |
| 174 VkDeviceAddress = distinct uint64 | 288 VkDeviceAddress = distinct uint64 |
| 175 VkRemoteAddressNV = pointer | 289 VkRemoteAddressNV = pointer |
| 176 """ | 290 """ |
| 177 | |
| 178 for t in types: | |
| 179 if t.attr("api") == "vulkansc": | |
| 180 continue | |
| 181 if t.attr("alias") != "": | |
| 182 continue | |
| 183 if t.attr("deprecated") == "true": | |
| 184 continue | |
| 185 if t.attr("category") == "include": | |
| 186 continue | |
| 187 if t.attr("category") == "define": | |
| 188 continue | |
| 189 if t.attr("category") == "bitmask": | |
| 190 if t.len > 0 and t[0].text.startsWith("typedef"): | |
| 191 outFile.writeLine &" {t[2][0].text} = distinct {t[1][0].text}" | |
| 192 elif t.attr("category") == "union": | |
| 193 let n = t.attr("name") | |
| 194 outFile.writeLine &" {n}* {{.union.}} = object" | |
| 195 for member in t.findAll("member"): | |
| 196 outFile.writeLine &" {member.memberDecl()}" | |
| 197 elif t.attr("category") == "handle": | |
| 198 outFile.writeLine &" {t[2][0].text} = distinct pointer" | |
| 199 elif t.attr("category") == "struct": | |
| 200 let n = t.attr("name") | |
| 201 outFile.writeLine &" {n}* = object" | |
| 202 for member in t.findAll("member"): | |
| 203 outFile.writeLine &" {member.memberDecl()}" | |
| 204 # TODO: funcpointer | |
| 205 | |
| 206 outFile.writeLine "" | |
| 207 | 291 |
| 208 # generate consts =============================================================================== | 292 # generate consts =============================================================================== |
| 209 outFile.writeLine "const" | 293 outFile.writeLine "const" |
| 210 for c in consts: | 294 for c in consts: |
| 211 var value = c.value | 295 var value = c.value |
| 217 value = "not " & value[1 ..^ 1] | 301 value = "not " & value[1 ..^ 1] |
| 218 outFile.writeLine &" {c.name}*: {c.datatype} = {value}" | 302 outFile.writeLine &" {c.name}*: {c.datatype} = {value}" |
| 219 outFile.writeLine "" | 303 outFile.writeLine "" |
| 220 | 304 |
| 221 # generate enums =============================================================================== | 305 # generate enums =============================================================================== |
| 306 const nameCollisions = [ | |
| 307 "VK_PIPELINE_CACHE_HEADER_VERSION_ONE", | |
| 308 "VK_PIPELINE_CACHE_HEADER_VERSION_SAFETY_CRITICAL_ONE", | |
| 309 "VK_DEVICE_FAULT_VENDOR_BINARY_HEADER_VERSION_ONE_EXT", | |
| 310 ] | |
| 222 outFile.writeLine "type" | 311 outFile.writeLine "type" |
| 223 for edef in enums.values(): | 312 for edef in enums.values(): |
| 224 if edef.values.len > 0: | 313 if edef.values.len > 0: |
| 225 outFile.writeLine &" {edef.name}* {{.size: 4.}} = enum" | 314 outFile.writeLine &" {edef.name}* {{.size: 4.}} = enum" |
| 226 for ee in edef.values: | 315 for ee in edef.values: |
| 227 outFile.writeLine &" {ee.name} = {ee.value}" | 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 | |
| 228 outFile.writeLine "" | 377 outFile.writeLine "" |
| 229 | 378 |
| 230 outFile.writeLine """ | 379 outFile.writeLine """ |
| 231 when defined(linux): | 380 when defined(linux): |
| 232 include ../semicongine/rendering/vulkan/platform/xlib | 381 include ../semicongine/rendering/vulkan/platform/xlib |
