78
|
1 import std/os
|
|
2 import std/sugar
|
|
3 import std/algorithm
|
|
4 import std/strformat
|
|
5 import std/strutils
|
|
6 import std/sequtils
|
|
7 import std/streams
|
|
8 import std/tables
|
|
9 import httpClient
|
|
10 import std/xmlparser
|
|
11 import std/xmltree
|
|
12
|
|
13 type
|
|
14 FileContent = seq[string]
|
|
15
|
|
16 const
|
|
17 TYPEMAP = {
|
|
18 "void": "void",
|
|
19 "char": "char",
|
|
20 "float": "float32",
|
|
21 "double": "float64",
|
|
22 "int8_t": "int8",
|
|
23 "uint8_t": "uint8",
|
|
24 "int16_t": "int16",
|
|
25 "uint16_t": "uint16",
|
|
26 "int32_t": "int32",
|
|
27 "uint32_t": "uint32",
|
|
28 "uint64_t": "uint64",
|
|
29 "int64_t": "int64",
|
|
30 "size_t": "csize_t",
|
|
31 "int": "cint",
|
|
32 "void*": "pointer",
|
|
33 "char*": "cstring",
|
|
34 "ptr char": "cstring",
|
|
35 "ptr void": "pointer",
|
|
36 "VK_DEFINE_HANDLE": "VkHandle",
|
|
37 "VK_DEFINE_NON_DISPATCHABLE_HANDLE": "VkNonDispatchableHandle",
|
|
38 }.toTable
|
|
39 PLATFORM_HEADER_MAP = {
|
79
|
40 "X11/Xlib.h": @["xlib", "xlib_xrandr"],
|
|
41 "X11/extensions/Xrandr.h": @["xlib_xrandr"],
|
|
42 "wayland-client.h": @["wayland"],
|
|
43 "windows.h": @["win32"],
|
|
44 "xcb/xcb.h": @["xcb"],
|
|
45 "directfb.h": @["directfb"],
|
|
46 "zircon/types.h": @["fuchsia"],
|
|
47 "ggp_c/vulkan_types.h": @["ggp"],
|
|
48 "screen/screen.h": @["screen"],
|
|
49 "nvscisync.h": @["sci"],
|
|
50 "nvscibuf.h": @["sci"],
|
|
51 "vk_video/vulkan_video_codec_h264std.h": @["provisional"],
|
|
52 "vk_video/vulkan_video_codec_h264std_decode.h": @["provisional"],
|
|
53 "vk_video/vulkan_video_codec_h264std_encode.h": @["provisional"],
|
|
54 "vk_video/vulkan_video_codec_h265std.h": @["provisional"],
|
|
55 "vk_video/vulkan_video_codec_h265std_decode.h": @["provisional"],
|
|
56 "vk_video/vulkan_video_codec_h265std_encode.h": @["provisional"],
|
78
|
57 }.toTable
|
|
58 MAP_KEYWORD = {
|
|
59 "object": "theobject",
|
|
60 "type": "thetype",
|
|
61 }.toTable
|
79
|
62 SPECIAL_DEPENDENCIES = {
|
|
63 "VK_NV_ray_tracing": "VK_KHR_ray_tracing_pipeline",
|
|
64 }.toTable
|
78
|
65
|
|
66 # helpers
|
|
67 func mapType(typename: string): auto =
|
|
68 TYPEMAP.getOrDefault(typename.strip(), typename.strip()).strip(chars={'_'})
|
|
69 func mapName(thename: string): auto =
|
|
70 MAP_KEYWORD.getOrDefault(thename.strip(), thename.strip()).strip(chars={'_'})
|
|
71 func smartParseInt(value: string): int =
|
|
72 if value.startsWith("0x"):
|
|
73 parseHexInt(value)
|
|
74 else:
|
|
75 parseInt(value)
|
|
76 func hasAttr(node: XmlNode, attr: string): bool = node.attr(attr) != ""
|
|
77 func tableSorted(table: Table[int, string]): seq[(int, string)] =
|
|
78 result = toSeq(table.pairs)
|
|
79 result.sort((a, b) => cmp(a[0], b[0]))
|
|
80
|
|
81 # serializers
|
80
|
82 func serializeEnum(node: XmlNode, api: XmlNode): seq[string] =
|
78
|
83 let name = node.attr("name")
|
|
84 if name == "":
|
|
85 return result
|
|
86
|
80
|
87 var reservedNames: seq[string]
|
|
88 for t in api.findAll("type"):
|
|
89 reservedNames.add t.attr("name").replace("_", "").toLower()
|
|
90
|
78
|
91 # find additional enum defintion in feature definitions
|
|
92 var values: Table[int, string]
|
80
|
93 for feature in api.findAll("feature"):
|
78
|
94 for require in feature.findAll("require"):
|
|
95 for theenum in require.findAll("enum"):
|
|
96 if theenum.attr("extends") == name:
|
|
97 if theenum.hasAttr("offset"):
|
|
98 let enumBase = 1000000000 + (smartParseInt(theenum.attr("extnumber")) - 1) * 1000
|
|
99 var value = smartParseInt(theenum.attr("offset")) + enumBase
|
|
100 if theenum.attr("dir") == "-":
|
|
101 value = -value
|
|
102 values[value] = theenum.attr("name")
|
|
103 elif theenum.hasAttr("value"):
|
|
104 var value = smartParseInt(theenum.attr("value"))
|
|
105 if theenum.attr("dir") == "-":
|
|
106 value = -value
|
|
107 values[value] = theenum.attr("name")
|
|
108 elif theenum.hasAttr("bitpos"):
|
|
109 var value = smartParseInt(theenum.attr("bitpos"))
|
|
110 if theenum.attr("dir") == "-":
|
|
111 value = -value
|
|
112 values[value] = theenum.attr("name")
|
|
113 elif theenum.hasAttr("alias"):
|
|
114 discard
|
|
115 else:
|
|
116 raise newException(Exception, &"Unknown extension value: {feature}\nvalue:{theenum}")
|
|
117 # find additional enum defintion in extension definitions
|
80
|
118 for extension in api.findAll("extension"):
|
78
|
119 let extensionNumber = parseInt(extension.attr("number"))
|
|
120 let enumBase = 1000000000 + (extensionNumber - 1) * 1000
|
|
121 for require in extension.findAll("require"):
|
|
122 for theenum in require.findAll("enum"):
|
|
123 if theenum.attr("extends") == name:
|
|
124 if theenum.hasAttr("offset"):
|
|
125 if theenum.hasAttr("extnumber"):
|
|
126 let otherBase = 1000000000 + (smartParseInt(theenum.attr("extnumber")) - 1) * 1000
|
|
127 var value = smartParseInt(theenum.attr("offset")) + otherBase
|
|
128 if theenum.attr("dir") == "-":
|
|
129 value = -value
|
|
130 values[value] = theenum.attr("name")
|
|
131 else:
|
|
132 var value = smartParseInt(theenum.attr("offset")) + enumBase
|
|
133 if theenum.attr("dir") == "-":
|
|
134 value = -value
|
|
135 values[value] = theenum.attr("name")
|
|
136 elif theenum.hasAttr("value"):
|
|
137 var value = smartParseInt(theenum.attr("value"))
|
|
138 if theenum.attr("dir") == "-":
|
|
139 value = -value
|
|
140 values[value] = theenum.attr("name")
|
|
141 elif theenum.hasAttr("bitpos"):
|
|
142 var value = smartParseInt(theenum.attr("bitpos"))
|
|
143 if theenum.attr("dir") == "-":
|
|
144 value = -value
|
|
145 values[value] = theenum.attr("name")
|
|
146 elif theenum.hasAttr("alias"):
|
|
147 discard
|
|
148 else:
|
|
149 raise newException(Exception, &"Unknown extension value: {extension}\nvalue:{theenum}")
|
|
150
|
|
151 # generate enums
|
|
152 if node.attr("type") == "enum":
|
|
153 for value in node.findAll("enum"):
|
|
154 if value.hasAttr("alias"):
|
|
155 continue
|
|
156 if value.attr("value").startsWith("0x"):
|
|
157 values[parseHexInt(value.attr("value"))] = value.attr("name")
|
|
158 else:
|
|
159 values[smartParseInt(value.attr("value"))] = value.attr("name")
|
|
160 if values.len > 0:
|
|
161 result.add " " & name & "* {.size: sizeof(cint).} = enum"
|
|
162 for (value, name) in tableSorted(values):
|
80
|
163 var thename = name
|
|
164 if name.replace("_", "").toLower() in reservedNames:
|
|
165 thename = thename & "_ENUM"
|
|
166 let enumEntry = &" {thename} = {value}"
|
78
|
167 result.add enumEntry
|
|
168
|
|
169 # generate bitsets (normal enums in the C API, but bitfield-enums in Nim)
|
|
170 elif node.attr("type") == "bitmask":
|
|
171 for value in node.findAll("enum"):
|
79
|
172 if value.hasAttr("bitpos"):
|
|
173 values[smartParseInt(value.attr("bitpos"))] = value.attr("name")
|
|
174 elif node.attr("name") == "VkVideoEncodeRateControlModeFlagBitsKHR": # special exception, for some reason this has values instead of bitpos
|
|
175 values[smartParseInt(value.attr("value"))] = value.attr("name")
|
78
|
176 if values.len > 0:
|
|
177 if node.hasAttr("bitwidth"):
|
|
178 result.add " " & name & "* {.size: " & $(smartParseInt(node.attr("bitwidth")) div 8) & ".} = enum"
|
|
179 else:
|
|
180 result.add " " & name & "* {.size: sizeof(cint).} = enum"
|
|
181 for (bitpos, enumvalue) in tableSorted(values):
|
|
182 var value = "00000000000000000000000000000000"# makes the bit mask nicely visible
|
|
183 if node.hasAttr("bitwidth"): # assumes this is always 64
|
|
184 value = value & value
|
|
185 value[^(bitpos + 1)] = '1'
|
|
186 let enumEntry = &" {enumvalue} = 0b{value}"
|
|
187 if not (enumEntry in result): # the specs define duplicate entries for backwards compat
|
|
188 result.add enumEntry
|
|
189 let cApiName = name.replace("FlagBits", "Flags")
|
|
190 if node.hasAttr("bitwidth"): # assumes this is always 64
|
|
191 if values.len > 0:
|
|
192 result.add &"""converter BitsetToNumber*(flags: openArray[{name}]): {cApiName} =
|
|
193 for flag in flags:
|
|
194 result = {cApiName}(uint64(result) or uint(flag))"""
|
|
195 result.add "type"
|
|
196 else:
|
|
197 if values.len > 0:
|
|
198 result.add &"""converter BitsetToNumber*(flags: openArray[{name}]): {cApiName} =
|
|
199 for flag in flags:
|
|
200 result = {cApiName}(uint(result) or uint(flag))"""
|
|
201 result.add "type"
|
|
202
|
80
|
203 func serializeStruct(node: XmlNode): seq[string] =
|
78
|
204 let name = node.attr("name")
|
|
205 var union = ""
|
|
206 if node.attr("category") == "union":
|
79
|
207 union = "{.union.} "
|
|
208 result.add &" {name}* {union}= object"
|
78
|
209 for member in node.findAll("member"):
|
|
210 if not member.hasAttr("api") or member.attr("api") == "vulkan":
|
|
211 let fieldname = member.child("name")[0].text.strip(chars={'_'})
|
|
212 var fieldtype = member.child("type")[0].text.strip(chars={'_'})
|
|
213 if member[member.len - 2].kind == xnText and member[member.len - 2].text.strip() == "*":
|
|
214 fieldtype = &"ptr {mapType(fieldtype)}"
|
|
215 fieldtype = mapType(fieldtype)
|
|
216 result.add &" {mapName(fieldname)}*: {fieldtype}"
|
|
217
|
|
218 func serializeFunctiontypes(api: XmlNode): seq[string] =
|
|
219 for node in api.findAll("type"):
|
|
220 if node.attr("category") == "funcpointer":
|
|
221 let name = node[1][0]
|
|
222 let returntype = mapType(node[0].text[8 .. ^1].split('(', 1)[0])
|
|
223 var params: seq[string]
|
|
224 for i in countup(3, node.len - 1, 2):
|
|
225 var paramname = node[i + 1].text.split(',', 1)[0].split(')', 1)[0]
|
|
226 var paramtype = node[i][0].text
|
|
227 if paramname[0] == '*':
|
|
228 paramname = paramname.rsplit(" ", 1)[1]
|
|
229 paramtype = "ptr " & paramtype
|
|
230 paramname = mapName(paramname)
|
|
231 params.add &"{paramname}: {mapType(paramtype)}"
|
|
232 let paramsstr = params.join(", ")
|
79
|
233 result.add(&" {name}* = proc({paramsstr}): {returntype} {{.cdecl.}}")
|
78
|
234
|
79
|
235 func serializeType(node: XmlNode, headerTypes: Table[string, string]): Table[string, seq[string]] =
|
78
|
236 if node.attrsLen == 0:
|
|
237 return
|
|
238 if node.attr("requires") == "vk_platform" or node.attr("category") == "include":
|
|
239 return
|
|
240 result["basetypes"] = @[]
|
|
241 result["enums"] = @[]
|
|
242
|
|
243 # include-defined types (in platform headers)
|
79
|
244 if node.attr("name") in headerTypes:
|
|
245 for platform in PLATFORM_HEADER_MAP[node.attr("requires")]:
|
|
246 let platformfile = "platform/" & platform
|
|
247 if not result.hasKey(platformfile):
|
|
248 result[platformfile] = @[]
|
|
249 result[platformfile].add " " & node.attr("name").strip(chars={'_'}) & " {.header: \"" & node.attr("requires") & "\".} = object"
|
78
|
250 # generic base types
|
|
251 elif node.attr("category") == "basetype":
|
|
252 let typechild = node.child("type")
|
|
253 let namechild = node.child("name")
|
|
254 if typechild != nil and namechild != nil:
|
|
255 var typename = typechild[0].text
|
|
256 if node[2].kind == xnText and node[2].text.strip() == "*":
|
|
257 typename = &"ptr {typename}"
|
|
258 result["basetypes"].add &" {namechild[0].text}* = {mapType(typename)}"
|
|
259 elif namechild != nil:
|
|
260 result["basetypes"].add &" {namechild[0].text}* = object"
|
|
261 # function pointers need to be handled with structs
|
|
262 elif node.attr("category") == "funcpointer":
|
|
263 discard
|
|
264 # preprocessor defines, ignored
|
|
265 elif node.attr("category") == "define":
|
|
266 discard
|
|
267 # bitmask aliases
|
|
268 elif node.attr("category") == "bitmask":
|
|
269 if node.hasAttr("alias"):
|
|
270 let name = node.attr("name")
|
|
271 let alias = node.attr("alias")
|
|
272 result["enums"].add &" {name}* = {alias}"
|
|
273 # distinct resource ID types aka handles
|
|
274 elif node.attr("category") == "handle":
|
|
275 if not node.hasAttr("alias"):
|
|
276 let name = node.child("name")[0].text
|
|
277 var thetype = mapType(node.child("type")[0].text)
|
|
278 result["basetypes"].add &" {name}* = distinct {thetype}"
|
|
279 # enum aliases
|
|
280 elif node.attr("category") == "enum":
|
|
281 if node.hasAttr("alias"):
|
|
282 let name = node.attr("name")
|
|
283 let alias = node.attr("alias")
|
|
284 result["enums"].add &" {name}* = {alias}"
|
|
285 else:
|
|
286 discard
|
|
287
|
79
|
288 func serializeCommand(node: XmlNode): (string, string) =
|
|
289 let
|
|
290 proto = node.child("proto")
|
|
291 resulttype = mapType(proto.child("type")[0].text)
|
|
292 name = proto.child("name")[0].text
|
|
293 var params: seq[string]
|
|
294 for param in node:
|
|
295 if param.tag == "param" and param.attr("api") in ["", "vulkan"]:
|
|
296 let fieldname = param.child("name")[0].text.strip(chars={'_'})
|
|
297 var fieldtype = param.child("type")[0].text.strip(chars={'_'})
|
|
298 if param[param.len - 2].kind == xnText and param[param.len - 2].text.strip() == "*":
|
|
299 fieldtype = &"ptr {mapType(fieldtype)}"
|
|
300 fieldtype = mapType(fieldtype)
|
|
301 params.add &"{mapName(fieldname)}: {fieldtype}"
|
|
302 let allparams = params.join(", ")
|
|
303 return (name, &"proc({allparams}): {resulttype} {{.stdcall.}}")
|
|
304
|
78
|
305
|
|
306 proc update(a: var Table[string, seq[string]], b: Table[string, seq[string]]) =
|
|
307 for k, v in b.pairs:
|
|
308 if not a.hasKey(k):
|
|
309 a[k] = @[]
|
|
310 a[k].add v
|
|
311
|
|
312
|
|
313 proc main() =
|
|
314 if not os.fileExists("vk.xml"):
|
|
315 let client = newHttpClient()
|
|
316 let glUrl = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml"
|
|
317 client.downloadFile(glUrl, "vk.xml")
|
|
318
|
|
319 let api = loadXml("vk.xml")
|
|
320
|
|
321 const outdir = "src/vulkan_api/output"
|
|
322 removeDir outdir
|
|
323 createDir outdir
|
|
324 createDir outdir / "platform"
|
|
325
|
|
326 # index all names that are only available on certain platforms
|
|
327 var platformTypes: Table[string, string]
|
|
328 for extension in api.findAll("extension"):
|
|
329 if extension.hasAttr("platform"):
|
|
330 for thetype in extension.findAll("type"):
|
|
331 platformTypes[thetype.attr("name")] = extension.attr("platform")
|
|
332 for command in extension.findAll("command"):
|
|
333 platformTypes[command.attr("name")] = extension.attr("platform")
|
|
334 elif extension.attr("name").startsWith("VK_KHR_video"):
|
|
335 for thetype in extension.findAll("type"):
|
79
|
336 platformTypes[thetype.attr("name")] = "provisional"
|
78
|
337 for command in extension.findAll("command"):
|
79
|
338 platformTypes[command.attr("name")] = "provisional"
|
78
|
339
|
|
340 var outputFiles = {
|
79
|
341 "basetypes": @[
|
|
342 "import std/dynlib",
|
|
343 "type",
|
|
344 " VkHandle* = distinct pointer",
|
|
345 " VkNonDispatchableHandle* = distinct pointer",
|
|
346 "when defined(linux):",
|
|
347 " let vulkanLib* = loadLib(\"libvulkan.so.1\")",
|
|
348 "when defined(windows):",
|
|
349 " let vulkanLib* = loadLib(\"vulkan-1.dll\")",
|
|
350 "if vulkanLib == nil:",
|
|
351 " raise newException(Exception, \"Unable to load vulkan library\")",
|
80
|
352 "func VK_MAKE_API_VERSION*(variant: uint32, major: uint32, minor: uint32, patch: uint32): uint32 {.compileTime.} =",
|
|
353 " (variant shl 29) or (major shl 22) or (minor shl 12) or patch",
|
|
354 "",
|
79
|
355 "type",
|
|
356 ],
|
|
357 "structs": @["type"],
|
|
358 "enums": @["type"],
|
|
359 "commands": @[],
|
78
|
360 }.toTable
|
|
361
|
|
362 # enums
|
|
363 for thetype in api.findAll("type"):
|
|
364 if thetype.attr("category") == "bitmask" and not thetype.hasAttr("alias") and (not thetype.hasAttr("api") or thetype.attr("api") == "vulkan"):
|
|
365 let name = thetype.child("name")[0].text
|
|
366 outputFiles["enums"].add &" {name}* = distinct VkFlags"
|
|
367 for theenum in api.findAll("enums"):
|
|
368 outputFiles["enums"].add serializeEnum(theenum, api)
|
|
369
|
|
370 # structs and function types need to be in same "type" block to avoid forward-declarations
|
|
371 outputFiles["structs"].add serializeFunctiontypes(api)
|
|
372 for thetype in api.findAll("type"):
|
|
373 if thetype.attr("category") == "struct" or thetype.attr("category") == "union":
|
|
374 var outfile = "structs"
|
|
375 if thetype.attr("name") in platformTypes:
|
|
376 outfile = "platform/" & platformTypes[thetype.attr("name")]
|
|
377 if not (outfile in outputFiles):
|
|
378 outputFiles[outfile] = @[]
|
80
|
379 outputFiles[outfile].add serializeStruct(thetype)
|
78
|
380
|
|
381 # types
|
79
|
382 var headerTypes: Table[string, string]
|
|
383 for types in api.findAll("types"):
|
|
384 for thetype in types.findAll("type"):
|
|
385 if thetype.attrsLen == 2 and thetype.hasAttr("requires") and thetype.hasAttr("name") and thetype.attr("requires") != "vk_platform":
|
|
386 let name = thetype.attr("name")
|
|
387 let incld = thetype.attr("requires")
|
|
388 headerTypes[name] = &"{name} {{.header: \"{incld}\".}} = object"
|
|
389
|
78
|
390 for typesgroup in api.findAll("types"):
|
|
391 for thetype in typesgroup.findAll("type"):
|
79
|
392 outputFiles.update serializeType(thetype, headerTypes)
|
|
393
|
|
394 # commands aka functions
|
|
395 var varDecls: Table[string, string]
|
|
396 var procLoads: Table[string, string] # procloads need to be packed into feature/extension loader procs
|
|
397 for commands in api.findAll("commands"):
|
|
398 for command in commands.findAll("command"):
|
|
399 if command.attr("api") != "vulkansc":
|
|
400 if command.hasAttr("alias"):
|
|
401 let name = command.attr("name")
|
|
402 let alias = command.attr("alias")
|
|
403 let thetype = varDecls[alias].split(":", 1)[1].strip()
|
|
404 varDecls[name] = &" {name}*: {thetype}"
|
|
405 procLoads[name] = &" {name} = {alias}"
|
|
406 else:
|
|
407 let (name, thetype) = serializeCommand(command)
|
|
408 varDecls[name] = &" {name}*: {thetype}"
|
|
409 procLoads[name] = &" {name} = cast[{thetype}](checkedSymAddr(vulkanLib, \"{name}\"))"
|
|
410 var declared: seq[string]
|
|
411 var featureloads: seq[string]
|
|
412 for feature in api.findAll("feature"):
|
|
413 if feature.attr("api") in ["vulkan", "vulkan,vulkansc"]:
|
|
414 let name = feature.attr("name")
|
|
415 outputFiles["commands"].add &"# feature {name}"
|
|
416 outputFiles["commands"].add "var"
|
|
417 for command in feature.findAll("command"):
|
|
418 if not (command.attr("name") in declared):
|
|
419 outputFiles["commands"].add varDecls[command.attr("name")]
|
|
420 declared.add command.attr("name")
|
|
421 featureloads.add &"load{name}"
|
|
422 outputFiles["commands"].add &"proc load{name}*() ="
|
|
423 for command in feature.findAll("command"):
|
|
424 outputFiles["commands"].add procLoads[command.attr("name")]
|
|
425 outputFiles["commands"].add ""
|
80
|
426 outputFiles["commands"].add ["proc initVulkan*() ="]
|
79
|
427 for l in featureloads:
|
|
428 outputFiles["commands"].add [&" {l}()"]
|
|
429 outputFiles["commands"].add ""
|
|
430
|
|
431 # for promoted extensions, dependants need to call the load-function of the promoted feature/extension
|
|
432 # use table to store promotions
|
|
433 var promotions: Table[string, string]
|
|
434 for extensions in api.findAll("extensions"):
|
|
435 for extension in extensions.findAll("extension"):
|
|
436 if extension.hasAttr("promotedto"):
|
|
437 promotions[extension.attr("name")] = extension.attr("promotedto")
|
|
438
|
|
439 var extensionDependencies: Table[string, (seq[string], XmlNode)]
|
|
440 var features: seq[string]
|
|
441 for feature in api.findAll("feature"):
|
|
442 features.add feature.attr("name")
|
|
443 for extensions in api.findAll("extensions"):
|
|
444 for extension in extensions.findAll("extension"):
|
|
445 let name = extension.attr("name")
|
|
446 extensionDependencies[name] = (@[], extension)
|
|
447 if extension.hasAttr("depends"):
|
|
448 extensionDependencies[name] = (extension.attr("depends").split("+"), extension)
|
|
449 if extension.attr("depends").startsWith("("): # no need for full tree parser, only single place where we can use a feature
|
|
450 let dependencies = extension.attr("depends").rsplit({')'}, 1)[1][1 .. ^1].split("+")
|
|
451 extensionDependencies[name] = (dependencies, extension)
|
|
452 if name in SPECIAL_DEPENDENCIES:
|
|
453 extensionDependencies[name][0].add SPECIAL_DEPENDENCIES[name]
|
|
454
|
|
455 var dependencyOrderedExtensions: OrderedTable[string, XmlNode]
|
|
456 while extensionDependencies.len > 0:
|
|
457 var delkeys: seq[string]
|
|
458 for extensionName, (dependencies, extension) in extensionDependencies.pairs:
|
|
459 var missingExtension = false
|
|
460 for dep in dependencies:
|
|
461 let realdep = promotions.getOrDefault(dep, dep)
|
|
462 if not (realdep in dependencyOrderedExtensions) and not (realdep in features):
|
|
463 missingExtension = true
|
|
464 break
|
|
465 if not missingExtension:
|
|
466 dependencyOrderedExtensions[extensionName] = extension
|
|
467 delkeys.add extensionName
|
|
468 for key in delkeys:
|
|
469 extensionDependencies.del key
|
|
470
|
|
471 for extension in dependencyOrderedExtensions.values:
|
|
472 if extension.hasAttr("promotedto"): # will be loaded in promoted place
|
|
473 continue
|
|
474 if extension.attr("supported") in ["", "vulkan", "vulkan,vulkansc"]:
|
|
475 var file = "commands"
|
|
476 if extension.attr("platform") != "":
|
|
477 file = "platform/" & extension.attr("platform")
|
|
478 elif extension.attr("name").startsWith("VK_KHR_video"): # hack since we do not include video headers by default
|
|
479 file = "platform/provisional"
|
|
480 let name = extension.attr("name")
|
|
481 if extension.findAll("command").len > 0:
|
|
482 outputFiles[file].add &"# extension {name}"
|
|
483 outputFiles[file].add "var"
|
|
484 for command in extension.findAll("command"):
|
|
485 if not (command.attr("name") in declared):
|
|
486 outputFiles[file].add varDecls[command.attr("name")]
|
|
487 declared.add command.attr("name")
|
|
488 outputFiles[file].add &"proc load{name}*() ="
|
|
489 var addedFunctionBody = false
|
|
490 if extension.hasAttr("depends"):
|
|
491 for dependency in extension.attr("depends").split("+"):
|
|
492 # need to check since some extensions have no commands and therefore no load-function
|
|
493 outputFiles[file].add &" load{promotions.getOrDefault(dependency, dependency)}()"
|
|
494 addedFunctionBody = true
|
|
495 for command in extension.findAll("command"):
|
|
496 outputFiles[file].add procLoads[command.attr("name")]
|
|
497 addedFunctionBody = true
|
|
498 if not addedFunctionBody:
|
|
499 outputFiles[file].add " discard"
|
|
500 outputFiles[file].add ""
|
|
501
|
|
502 var mainout: seq[string]
|
|
503 for section in ["basetypes", "enums", "structs", "commands"]:
|
|
504 mainout.add outputFiles[section]
|
80
|
505 for platform in api.findAll("platform"):
|
|
506 mainout.add &"when defined({platform.attr(\"protect\")}):"
|
|
507 mainout.add &" include platform/{platform.attr(\"name\")}"
|
79
|
508 writeFile outdir / &"types.nim", mainout.join("\n")
|
|
509
|
78
|
510 for filename, filecontent in outputFiles.pairs:
|
79
|
511 if filename.startsWith("platform/"):
|
|
512 writeFile outdir / &"{filename}.nim", (@[
|
|
513 "type"
|
|
514 ] & filecontent).join("\n")
|
78
|
515
|
|
516 when isMainModule:
|
|
517 main()
|