scripts: Use named params for generator options
[platform/upstream/Vulkan-Tools.git] / scripts / vulkaninfo_generator.py
1 #!/usr/bin/python3
2 #
3 # Copyright (c) 2019 Valve Corporation
4 # Copyright (c) 2019 LunarG, Inc.
5 # Copyright (c) 2019 Google Inc.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #     http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 # Author: Charles Giessen <charles@lunarg.com>
20
21 import os
22 import re
23 import sys
24 import string
25 import xml.etree.ElementTree as etree
26 import generator as gen
27 import operator
28 import json
29 from collections import namedtuple
30 from collections import OrderedDict
31 from generator import *
32 from common_codegen import *
33
34 license_header = '''
35 /*
36  * Copyright (c) 2019 The Khronos Group Inc.
37  * Copyright (c) 2019 Valve Corporation
38  * Copyright (c) 2019 LunarG, Inc.
39  *
40  * Licensed under the Apache License, Version 2.0 (the "License");
41  * you may not use this file except in compliance with the License.
42  * You may obtain a copy of the License at
43  *
44  *     http://www.apache.org/licenses/LICENSE-2.0
45  *
46  * Unless required by applicable law or agreed to in writing, software
47  * distributed under the License is distributed on an "AS IS" BASIS,
48  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49  * See the License for the specific language governing permissions and
50  * limitations under the License.
51  *
52  * Author: Charles Giessen <charles@lunarg.com>
53  *
54  */
55
56 /*
57  * This file is generated from the Khronos Vulkan XML API Registry.
58  */
59 '''
60
61 custom_formaters = r'''
62 void DumpVkConformanceVersion(Printer &p, std::string name, VkConformanceVersion &c, int width = 0) {
63     p.PrintKeyString("conformanceVersion", std::to_string(c.major)+ "." + std::to_string(c.minor) + "." + std::to_string(c.subminor) + "."
64              + std::to_string(c.patch), width);
65 }
66
67 template <typename T>
68 std::string to_hex_str(T i) {
69     std::stringstream stream;
70     stream << "0x" << std::setfill('0') << std::setw(sizeof(T)) << std::hex << i;
71     return stream.str();
72 }
73
74 template <typename T>
75 std::string to_hex_str(Printer &p, T i) {
76     if (p.Type() == OutputType::json)
77         return std::to_string(i);
78     else if (p.Type() == OutputType::vkconfig_output)
79         return std::string("\"") + to_hex_str(i) + std::string("\"");
80     else
81         return to_hex_str(i);
82 }
83
84 '''
85
86
87 # used in the .cpp code
88 structures_to_gen = ['VkExtent3D', 'VkExtent2D', 'VkPhysicalDeviceLimits', 'VkPhysicalDeviceFeatures', 'VkPhysicalDeviceSparseProperties',
89                      'VkSurfaceCapabilitiesKHR', 'VkSurfaceFormatKHR', 'VkLayerProperties', 'VkPhysicalDeviceToolPropertiesEXT']
90 enums_to_gen = ['VkResult', 'VkFormat', 'VkPresentModeKHR',
91                 'VkPhysicalDeviceType', 'VkImageTiling']
92 flags_to_gen = ['VkSurfaceTransformFlagsKHR', 'VkCompositeAlphaFlagsKHR', 'VkSurfaceCounterFlagsEXT',
93                 'VkDeviceGroupPresentModeFlagsKHR', 'VkFormatFeatureFlags', 'VkMemoryPropertyFlags', 'VkMemoryHeapFlags']
94 flags_strings_to_gen = ['VkQueueFlags']
95
96 struct_short_versions_to_gen = ['VkExtent3D']
97
98 struct_comparisons_to_gen = ['VkSurfaceFormatKHR', 'VkSurfaceFormat2KHR', 'VkSurfaceCapabilitiesKHR',
99                              'VkSurfaceCapabilities2KHR', 'VkSurfaceCapabilities2EXT']
100 # don't generate these structures
101 struct_blacklist = ['VkConformanceVersion']
102
103 # iostream or custom outputter handles these types
104 predefined_types = ['char', 'VkBool32', 'uint32_t', 'uint8_t', 'int32_t',
105                     'float', 'uint64_t', 'size_t', 'VkDeviceSize']
106
107 # Types that need pNext Chains built. 'extends' is the xml tag used in the structextends member. 'type' can be device, instance, or both
108 EXTENSION_CATEGORIES = OrderedDict((('phys_device_props2', {'extends': 'VkPhysicalDeviceProperties2', 'type': 'both'}),
109                                    ('phys_device_mem_props2', {'extends': 'VkPhysicalDeviceMemoryProperties2', 'type': 'device'}),
110                                    ('phys_device_features2', {'extends': 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo', 'type': 'device'}),
111                                    ('surface_capabilities2', {'extends': 'VkSurfaceCapabilities2KHR', 'type': 'both'}),
112                                    ('format_properties2', {'extends': 'VkFormatProperties2', 'type': 'device'})
113                                    ))
114 class VulkanInfoGeneratorOptions(GeneratorOptions):
115     def __init__(self,
116                  conventions=None,
117                  input=None,
118                  filename=None,
119                  directory='.',
120                  apiname=None,
121                  profile=None,
122                  versions='.*',
123                  emitversions='.*',
124                  defaultExtensions=None,
125                  addExtensions=None,
126                  removeExtensions=None,
127                  emitExtensions=None,
128                  sortProcedure=None,
129                  prefixText="",
130                  genFuncPointers=True,
131                  protectFile=True,
132                  protectFeature=True,
133                  protectProto=None,
134                  protectProtoStr=None,
135                  apicall='',
136                  apientry='',
137                  apientryp='',
138                  indentFuncProto=True,
139                  indentFuncPointer=False,
140                  alignFuncParam=0,
141                  expandEnumerants=True,
142                  ):
143         GeneratorOptions.__init__(self,
144                  conventions = conventions,
145                  filename = filename,
146                  directory = directory,
147                  apiname = apiname,
148                  profile = profile,
149                  versions = versions,
150                  emitversions = emitversions,
151                  defaultExtensions = defaultExtensions,
152                  addExtensions = addExtensions,
153                  removeExtensions = removeExtensions,
154                  emitExtensions = emitExtensions,
155                  sortProcedure = sortProcedure)
156         self.input = input
157         self.prefixText = prefixText
158         self.genFuncPointers = genFuncPointers
159         self.protectFile = protectFile
160         self.protectFeature = protectFeature
161         self.protectProto = protectProto
162         self.protectProtoStr = protectProtoStr
163         self.apicall = apicall
164         self.apientry = apientry
165         self.apientryp = apientryp
166         self.indentFuncProto = indentFuncProto
167         self.indentFuncPointer = indentFuncPointer
168         self.alignFuncParam = alignFuncParam
169
170 # VulkanInfoGenerator - subclass of OutputGenerator.
171 # Generates a vulkan info output helper function
172
173
174 class VulkanInfoGenerator(OutputGenerator):
175
176     def __init__(self,
177                  errFile=sys.stderr,
178                  warnFile=sys.stderr,
179                  diagFile=sys.stdout):
180         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
181
182         self.constants = OrderedDict()
183
184         self.types_to_gen = set()
185
186         self.extension_sets = OrderedDict()
187         for ext_cat in EXTENSION_CATEGORIES.keys():
188             self.extension_sets[ext_cat] = set()
189
190         self.enums = []
191         self.flags = []
192         self.bitmasks = []
193         self.all_structures = []
194         self.aliases = OrderedDict()
195
196         self.extFuncs = OrderedDict()
197         self.extTypes = OrderedDict()
198
199         self.vendor_abbreviations = []
200         self.vulkan_versions = []
201
202     def beginFile(self, genOpts):
203         gen.OutputGenerator.beginFile(self, genOpts)
204
205         for node in self.registry.reg.findall('enums'):
206             if node.get('name') == 'API Constants':
207                 for item in node.findall('enum'):
208                     self.constants[item.get('name')] = item.get('value')
209
210         for node in self.registry.reg.find('extensions').findall('extension'):
211             ext = VulkanExtension(node)
212             for item in ext.vktypes:
213                 self.extTypes[item] = ext
214             for item in ext.vkfuncs:
215                 self.extFuncs[item] = ext
216
217         # need list of venders to blacklist vendor extensions
218         for tag in self.registry.reg.find('tags'):
219             if tag.get("name") not in ["KHR", "EXT"]:
220                 self.vendor_abbreviations.append("_" + tag.get('name'))
221
222         for ver in self.registry.reg.findall('feature'):
223             self.vulkan_versions.append(VulkanVersion(ver))
224
225     def endFile(self):
226         # gather the types that are needed to generate
227         types_to_gen = set()
228         for s in enums_to_gen:
229             types_to_gen.add(s)
230
231         for f in flags_to_gen:
232             types_to_gen.add(f)
233
234         types_to_gen.update(
235             GatherTypesToGen(self.all_structures, structures_to_gen))
236         for key in EXTENSION_CATEGORIES.keys():
237             types_to_gen.update(
238                 GatherTypesToGen(self.all_structures, self.extension_sets[key]))
239         types_to_gen = sorted(types_to_gen)
240
241         names_of_structures_to_gen = set()
242         for s in self.all_structures:
243             if s.name in types_to_gen:
244                 names_of_structures_to_gen.add(s.name)
245         names_of_structures_to_gen = sorted(names_of_structures_to_gen)
246
247         structs_to_comp = set()
248         for s in struct_comparisons_to_gen:
249             structs_to_comp.add(s)
250         structs_to_comp.update(
251             GatherTypesToGen(self.all_structures, struct_comparisons_to_gen))
252
253         for key, value in self.extension_sets.items():
254             self.extension_sets[key] = sorted(value)
255
256         alias_versions = OrderedDict()
257         for version in self.vulkan_versions:
258             for aliased_type, aliases in self.aliases.items():
259                 for alias in aliases:
260                     if alias in version.names:
261                         alias_versions[alias] = version.minorVersion
262
263         self.enums = sorted(self.enums, key=operator.attrgetter('name'))
264         self.flags = sorted(self.flags, key=operator.attrgetter('name'))
265         self.bitmasks = sorted(self.bitmasks, key=operator.attrgetter('name'))
266         self.all_structures = sorted(self.all_structures, key=operator.attrgetter('name'))
267
268         # print the types gathered
269         out = ''
270         out += license_header + "\n"
271         out += "#include \"vulkaninfo.h\"\n"
272         out += "#include \"outputprinter.h\"\n"
273         out += custom_formaters
274
275         for enum in (e for e in self.enums if e.name in types_to_gen):
276             out += PrintEnumToString(enum, self)
277             out += PrintEnum(enum, self)
278
279         for flag in self.flags:
280             if flag.name in types_to_gen:
281                 for bitmask in (b for b in self.bitmasks if b.name == flag.enum):
282                     out += PrintBitMask(bitmask, flag.name, self)
283
284             if flag.name in flags_strings_to_gen:
285                 for bitmask in (b for b in self.bitmasks if b.name == flag.enum):
286                     out += PrintBitMaskToString(bitmask, flag.name, self)
287
288         for s in (x for x in self.all_structures if x.name in types_to_gen and x.name not in struct_blacklist):
289             out += PrintStructure(s, types_to_gen, names_of_structures_to_gen)
290
291         out += "pNextChainInfos get_chain_infos() {\n"
292         out += "    pNextChainInfos infos;\n"
293         for key in EXTENSION_CATEGORIES.keys():
294             out += PrintChainBuilders(key,
295                                       self.extension_sets[key], self.all_structures)
296         out += "    return infos;\n}\n"
297
298         for key, value in EXTENSION_CATEGORIES.items():
299             out += PrintChainIterator(key,
300                                       self.extension_sets[key], self.all_structures, value.get('type'), self.extTypes, self.aliases, self.vulkan_versions)
301
302         for s in (x for x in self.all_structures if x.name in structs_to_comp):
303             out += PrintStructComparisonForwardDecl(s)
304         for s in (x for x in self.all_structures if x.name in structs_to_comp):
305             out += PrintStructComparison(s)
306         for s in (x for x in self.all_structures if x.name in struct_short_versions_to_gen):
307             out += PrintStructShort(s)
308
309         gen.write(out, file=self.outFile)
310
311         gen.OutputGenerator.endFile(self)
312
313     def genCmd(self, cmd, name, alias):
314         gen.OutputGenerator.genCmd(self, cmd, name, alias)
315
316     # These are actually constants
317     def genEnum(self, enuminfo, name, alias):
318         gen.OutputGenerator.genEnum(self, enuminfo, name, alias)
319
320     # These are actually enums
321     def genGroup(self, groupinfo, groupName, alias):
322         gen.OutputGenerator.genGroup(self, groupinfo, groupName, alias)
323
324         if alias is not None:
325             if alias in self.aliases.keys():
326                 self.aliases[alias].append(groupName)
327             else:
328                 self.aliases[alias] = [groupName, ]
329             return
330
331         if groupinfo.elem.get('type') == 'bitmask':
332             self.bitmasks.append(VulkanBitmask(groupinfo.elem))
333         elif groupinfo.elem.get('type') == 'enum':
334             self.enums.append(VulkanEnum(groupinfo.elem))
335
336     def genType(self, typeinfo, name, alias):
337         gen.OutputGenerator.genType(self, typeinfo, name, alias)
338
339         if alias is not None:
340             if alias in self.aliases.keys():
341                 self.aliases[alias].append(name)
342             else:
343                 self.aliases[alias] = [name, ]
344             return
345
346         if typeinfo.elem.get('category') == 'bitmask':
347             self.flags.append(VulkanFlags(typeinfo.elem))
348
349         if typeinfo.elem.get('category') == 'struct':
350             self.all_structures.append(VulkanStructure(
351                 name, typeinfo.elem, self.constants, self.extTypes))
352
353         for vendor in self.vendor_abbreviations:
354             for node in typeinfo.elem.findall('member'):
355                 if(node.get('values') is not None):
356                     if(node.get('values').find(vendor)) != -1:
357                         return
358
359         for key, value in EXTENSION_CATEGORIES.items():
360             if typeinfo.elem.get('structextends') == value.get('extends'):
361                 self.extension_sets[key].add(name)
362
363
364 def GatherTypesToGen(structure_list, structures):
365     types = set()
366     added_stuff = True  # repeat until no new types are added
367     while added_stuff == True:
368         added_stuff = False
369         for s in (x for x in structure_list if x.name in structures):
370             size = len(types)
371             types.add(s.name)
372             if len(types) != size:
373                 added_stuff = True
374             for m in s.members:
375                 if m.typeID not in predefined_types and m.name not in ['sType', 'pNext']:
376                     types.add(m.typeID)
377     types = sorted(types)
378     return types
379
380
381 def GetExtension(name, generator):
382     if name in generator.extFuncs:
383         return generator.extFuncs[name]
384     elif name in generator.extTypes:
385         return generator.extTypes[name]
386     else:
387         return None
388
389
390 def AddGuardHeader(obj):
391     if obj is not None and obj.guard is not None:
392         return "#ifdef {}\n".format(obj.guard)
393     else:
394         return ""
395
396
397 def AddGuardFooter(obj):
398     if obj is not None and obj.guard is not None:
399         return "#endif  // {}\n".format(obj.guard)
400     else:
401         return ""
402
403
404 def PrintEnumToString(enum, gen):
405     out = ''
406     out += AddGuardHeader(GetExtension(enum.name, gen))
407
408     out += "static const char *" + enum.name + \
409         "String(" + enum.name + " value) {\n"
410     out += "    switch (value) {\n"
411     for v in enum.options:
412         out += "        case (" + str(v.value) + \
413             "): return \"" + v.name[3:] + "\";\n"
414     out += "        default: return \"UNKNOWN_" + enum.name + "\";\n"
415     out += "    }\n}\n"
416     out += AddGuardFooter(GetExtension(enum.name, gen))
417     return out
418
419
420 def PrintEnum(enum, gen):
421     out = ''
422     out += AddGuardHeader(GetExtension(enum.name, gen))
423     out += "void Dump" + enum.name + \
424         "(Printer &p, std::string name, " + \
425         enum.name + " value, int width = 0) {\n"
426     out += "    if (p.Type() == OutputType::json) {\n"
427     out += "        p.PrintKeyValue(name, value, width);\n"
428     out += "    } else {\n"
429     out += "        p.PrintKeyString(name, " + \
430         enum.name + "String(value), width);\n    }\n"
431     out += "}\n"
432     out += AddGuardFooter(GetExtension(enum.name, gen))
433     return out
434
435
436 def PrintGetFlagStrings(name, bitmask):
437     out = ''
438     out += "std::vector<const char *>" + name + \
439         "GetStrings(" + name + " value) {\n"
440
441     out += "    std::vector<const char *> strings;\n"
442     out += "    if (value == 0) strings.push_back(\"None\");\n"
443     for v in bitmask.options:
444         val = v.value if isinstance(v.value, str) else str(hex(v.value))
445         out += "    if (" + val + " & value) strings.push_back(\"" + \
446             str(v.name[3:]) + "\");\n"
447     out += "    return strings;\n}\n"
448     return out
449
450
451 def PrintFlags(bitmask, name):
452     out = "void Dump" + name + \
453         "(Printer &p, std::string name, " + name + " value, int width = 0) {\n"
454     out += "    if (p.Type() == OutputType::json) { p.PrintKeyValue(name, value); return; }\n"
455     out += "    auto strings = " + bitmask.name + \
456         "GetStrings(static_cast<" + bitmask.name + ">(value));\n"
457     out += "    if (static_cast<" + bitmask.name + ">(value) == 0) {\n"
458     out += "        ArrayWrapper arr(p, name, 0);\n"
459     out += "        p.SetAsType().PrintString(\"None\");\n"
460     out += "        return;\n"
461     out += "    }\n"
462     out += "    ArrayWrapper arr(p, name, strings.size());\n"
463     out += "    for(auto& str : strings){\n"
464     out += "        p.SetAsType().PrintString(str);\n"
465     out += "    }\n"
466     out += "}\n"
467     return out
468
469
470 def PrintFlagBits(bitmask):
471     out = "void Dump" + bitmask.name + \
472         "(Printer &p, std::string name, " + \
473         bitmask.name + " value, int width = 0) {\n"
474     out += "    auto strings = " + bitmask.name + "GetStrings(value);\n"
475     out += "    p.PrintKeyString(name, strings.at(0), width);\n"
476     out += "}\n"
477     return out
478
479
480 def PrintBitMask(bitmask, name, gen):
481     out = PrintGetFlagStrings(bitmask.name, bitmask)
482     out += AddGuardHeader(GetExtension(bitmask.name, gen))
483     out += PrintFlags(bitmask, name)
484     out += PrintFlagBits(bitmask)
485     out += AddGuardFooter(GetExtension(bitmask.name, gen))
486     out += "\n"
487     return out
488
489
490 def PrintBitMaskToString(bitmask, name, gen):
491     out = AddGuardHeader(GetExtension(bitmask.name, gen))
492     out += "std::string " + name + \
493         "String(" + name + " value, int width = 0) {\n"
494     out += "    std::string out;\n"
495     out += "    bool is_first = true;\n"
496     for v in bitmask.options:
497         out += "    if (" + str(v.value) + " & value) {\n"
498         out += "        if (is_first) { is_first = false; } else { out += \" | \"; }\n"
499         out += "        out += \"" + \
500             str(v.name).strip("VK_").strip("_BIT") + "\";\n"
501         out += "    }\n"
502     out += "    return out;\n"
503     out += "}\n"
504     out += AddGuardFooter(GetExtension(bitmask.name, gen))
505     return out
506
507
508 def PrintStructure(struct, types_to_gen, structure_names):
509     if len(struct.members) == 0:
510         return ""
511     out = ''
512     out += AddGuardHeader(struct)
513     max_key_len = 0
514     for v in struct.members:
515         if v.arrayLength is not None:
516             if len(v.name) + len(v.arrayLength) + 2 > max_key_len:
517                 max_key_len = len(v.name) + len(v.arrayLength) + 2
518         elif v.typeID in predefined_types or v.typeID in struct_blacklist:
519             if len(v.name) > max_key_len:
520                 max_key_len = len(v.name)
521
522     out += "void Dump" + struct.name + \
523         "(Printer &p, std::string name, " + struct.name + " &obj) {\n"
524     if struct.name == "VkPhysicalDeviceLimits":
525         out += "    if (p.Type() == OutputType::json)\n"
526         out += "        p.ObjectStart(\"limits\");\n"
527         out += "    else\n"
528         out += "        p.SetSubHeader().ObjectStart(name);\n"
529     elif struct.name == "VkPhysicalDeviceSparseProperties":
530         out += "    if (p.Type() == OutputType::json)\n"
531         out += "        p.ObjectStart(\"sparseProperties\");\n"
532         out += "    else\n"
533         out += "        p.SetSubHeader().ObjectStart(name);\n"
534     else:
535         out += "    ObjectWrapper object{p, name};\n"
536
537     for v in struct.members:
538         # arrays
539         if v.arrayLength is not None:
540             # strings
541             if v.typeID == "char":
542                 out += "    p.PrintKeyString(\"" + v.name + "\", obj." + \
543                     v.name + ", " + str(max_key_len) + ");\n"
544             # uuid's
545             elif (v.arrayLength == str(16) and v.typeID == "uint8_t"):  # VK_UUID_SIZE
546                 out += "    p.PrintKeyString(\"" + v.name + "\", to_string_16(obj." + \
547                     v.name + "), " + str(max_key_len) + ");\n"
548             elif (v.arrayLength == str(8) and v.typeID == "uint8_t"):  # VK_LUID_SIZE
549                 out += "    if (obj.deviceLUIDValid)"  # special case
550                 out += " p.PrintKeyString(\"" + v.name + "\", to_string_8(obj." + \
551                     v.name + "), " + str(max_key_len) + ");\n"
552             elif v.arrayLength.isdigit():
553                 out += "    {   ArrayWrapper arr(p,\"" + v.name + \
554                     "\", "+v.arrayLength+");\n"
555                 for i in range(0, int(v.arrayLength)):
556                     out += "        p.PrintElement(obj." + \
557                         v.name + "[" + str(i) + "]);\n"
558                 out += "    }\n"
559             else:  # dynamic array length based on other member
560                 out += "    ArrayWrapper arr(p,\"" + v.name + \
561                     "\", obj."+v.arrayLength+");\n"
562                 out += "    for (uint32_t i = 0; i < obj." + \
563                     v.arrayLength+"; i++) {\n"
564                 if v.typeID in types_to_gen:
565                     out += "        if (obj." + v.name + " != nullptr) {\n"
566                     out += "            p.SetElementIndex(i);\n"
567                     out += "            Dump" + v.typeID + \
568                         "(p, \"" + v.name + "\", obj." + v.name + "[i]);\n"
569                     out += "        }\n"
570                 else:
571                     out += "        p.PrintElement(obj." + v.name + "[i]);\n"
572                 out += "    }\n"
573         elif v.typeID == "VkBool32":
574             out += "    p.PrintKeyBool(\"" + v.name + "\", static_cast<bool>(obj." + \
575                 v.name + "), " + str(max_key_len) + ");\n"
576         elif v.typeID == "VkConformanceVersion":
577             out += "    DumpVkConformanceVersion(p, \"conformanceVersion\", obj." + \
578                 v.name + ", " + str(max_key_len) + ");\n"
579         elif v.typeID == "VkDeviceSize":
580             out += "    p.PrintKeyValue(\"" + v.name + "\", to_hex_str(p, obj." + \
581                 v.name + "), " + str(max_key_len) + ");\n"
582         elif v.typeID in predefined_types:
583             out += "    p.PrintKeyValue(\"" + v.name + "\", obj." + \
584                 v.name + ", " + str(max_key_len) + ");\n"
585         elif v.name not in ['sType', 'pNext']:
586             # if it is an enum/flag/bitmask, add the calculated width
587             if v.typeID not in structure_names:
588                 out += "    Dump" + v.typeID + \
589                     "(p, \"" + v.name + "\", obj." + \
590                     v.name + ", " + str(max_key_len) + ");\n"
591             else:
592                 out += "    Dump" + v.typeID + \
593                     "(p, \"" + v.name + "\", obj." + v.name + ");\n"
594     if struct.name in ["VkPhysicalDeviceLimits", "VkPhysicalDeviceSparseProperties"]:
595         out += "    p.ObjectEnd();\n"
596     out += "}\n"
597
598     out += AddGuardFooter(struct)
599     return out
600
601
602 def PrintStructShort(struct):
603     out = ''
604     out += AddGuardHeader(struct)
605     out += "std::ostream &operator<<(std::ostream &o, " + \
606         struct.name + " &obj) {\n"
607     out += "    return o << \"(\" << "
608
609     first = True
610     for v in struct.members:
611         if first:
612             first = False
613             out += "obj." + v.name + " << "
614         else:
615             out += "\',\' << obj." + v.name + " << "
616     out += "\")\";\n"
617     out += "}\n"
618     out += AddGuardFooter(struct)
619     return out
620
621
622 def PrintChainBuilders(listName, structures, all_structures):
623     sorted_structures = sorted(
624         all_structures, key=operator.attrgetter('name'))
625
626     out = ''
627     out += "    infos." + listName + " = {\n"
628     for s in sorted_structures:
629         if s.name in structures:
630             out += AddGuardHeader(s)
631             if s.sTypeName is not None:
632                 out += "        {" + s.sTypeName + \
633                     ", sizeof(" + s.name + ")},\n"
634             out += AddGuardFooter(s)
635     out += "    };\n"
636     return out
637
638
639 def PrintChainIterator(listName, structures, all_structures, checkExtLoc, extTypes, aliases, versions):
640     out = ''
641     out += "void chain_iterator_" + listName + "(Printer &p, "
642     if checkExtLoc == "device":
643         out += "AppGpu &gpu"
644     elif checkExtLoc == "instance":
645         out += "AppInstance &inst"
646     elif checkExtLoc == "both":
647         out += "AppInstance &inst, AppGpu &gpu"
648     out += ", void * place, VulkanVersion version) {\n"
649
650     out += "    while (place) {\n"
651     out += "        struct VkStructureHeader *structure = (struct VkStructureHeader *)place;\n"
652     out += "        p.SetSubHeader();\n"
653     sorted_structures = sorted(
654         all_structures, key=operator.attrgetter('name'))
655     for s in sorted_structures:
656         if s.sTypeName is None:
657             continue
658
659         extNameStr = None
660         extType = None
661         for k, e in extTypes.items():
662             if k == s.name or (s.name in aliases.keys() and k in aliases[s.name]):
663                 if e.extNameStr is not None:
664                     extNameStr = e.extNameStr
665                 if e.type is not None:
666                     extType = e.type
667                 break
668         version = None
669         oldVersionName = None
670         for v in versions:
671             if s.name in v.names:
672                 version = v.minorVersion
673         if s.name in aliases.keys():
674             for alias in aliases[s.name]:
675                 oldVersionName = alias
676
677         if s.name in structures:
678             out += AddGuardHeader(s)
679             out += "        if (structure->sType == " + s.sTypeName
680             has_version = version is not None
681             has_extNameStr = extNameStr is not None or s.name in aliases.keys()
682
683             if has_version or has_extNameStr:
684                 out += " && \n           ("
685                 if has_extNameStr:
686                     if extType == "device":
687                         out += "gpu.CheckPhysicalDeviceExtensionIncluded(" + \
688                             extNameStr + ")"
689                     elif extType == "instance":
690                         out += "inst.CheckExtensionEnabled(" + extNameStr + ")"
691                     if has_version and extType is not None:
692                         out += " ||\n            "
693                 if has_version:
694                     out += "version.minor >= " + str(version)
695                 out += ")"
696             out += ") {\n"
697             out += "            " + s.name + "* props = " + \
698                 "("+s.name+"*)structure;\n"
699
700             out += "            Dump" + s.name + "(p, "
701             if s.name in aliases.keys() and version is not None:
702                 out += "version.minor >= " + version + " ?\"" + \
703                     s.name + "\":\"" + oldVersionName + "\""
704             else:
705                 out += "\"" + s.name + "\""
706             out += ", *props);\n"
707             out += "            p.AddNewline();\n"
708             out += "        }\n"
709             out += AddGuardFooter(s)
710     out += "        place = structure->pNext;\n"
711     out += "    }\n"
712     out += "}\n"
713     return out
714
715 def PrintStructComparisonForwardDecl(structure):
716     out = ''
717     out += "bool operator==(const " + structure.name + \
718         " & a, const " + structure.name + " b);\n"
719     return out
720
721
722 def PrintStructComparison(structure):
723     out = ''
724     out += "bool operator==(const " + structure.name + \
725         " & a, const " + structure.name + " b) {\n"
726     out += "    return "
727     is_first = True
728     for m in structure.members:
729         if m.name not in ['sType', 'pNext']:
730             if not is_first:
731                 out += "\n        && "
732             else:
733                 is_first = False
734             out += "a." + m.name + " == b." + m.name
735     out += ";\n"
736     out += "}\n"
737     return out
738
739
740 class VulkanEnum:
741     class Option:
742
743         def __init__(self, name, value, bitpos, comment):
744             self.name = name
745             self.comment = comment
746
747             if value == 0 or value is None:
748                 value = 1 << int(bitpos)
749
750             self.value = value
751
752         def values(self):
753             return {
754                 'optName': self.name,
755                 'optValue': self.value,
756                 'optComment': self.comment,
757             }
758
759     def __init__(self, rootNode):
760         self.name = rootNode.get('name')
761         self.type = rootNode.get('type')
762         self.options = []
763
764         for child in rootNode:
765             childName = child.get('name')
766             childValue = child.get('value')
767             childBitpos = child.get('bitpos')
768             childComment = child.get('comment')
769             childExtends = child.get('extends')
770             childOffset = child.get('offset')
771             childExtNum = child.get('extnumber')
772             support = child.get('supported')
773             if(support == "disabled"):
774                 continue
775
776             if childName is None:
777                 continue
778             if (childValue is None and childBitpos is None and childOffset is None):
779                 continue
780
781             if childExtends is not None and childExtNum is not None and childOffset is not None:
782                 enumNegative = False
783                 extNum = int(childExtNum)
784                 extOffset = int(childOffset)
785                 extBase = 1000000000
786                 extBlockSize = 1000
787                 childValue = extBase + (extNum - 1) * extBlockSize + extOffset
788                 if ('dir' in child.keys()):
789                     childValue = -childValue
790             duplicate = False
791             for o in self.options:
792                 if o.values()['optName'] == childName:
793                     duplicate = True
794             if duplicate:
795                 continue
796
797             self.options.append(VulkanEnum.Option(
798                 childName, childValue, childBitpos, childComment))
799
800
801 class VulkanBitmask:
802
803     def __init__(self, rootNode):
804         self.name = rootNode.get('name')
805         self.type = rootNode.get('type')
806
807         # Read each value that the enum contains
808         self.options = []
809         for child in rootNode:
810             childName = child.get('name')
811             childValue = child.get('value')
812             childBitpos = child.get('bitpos')
813             childComment = child.get('comment')
814             support = child.get('supported')
815             if childName is None or (childValue is None and childBitpos is None):
816                 continue
817             if(support == "disabled"):
818                 continue
819
820             duplicate = False
821             for option in self.options:
822                 if option.name == childName:
823                     duplicate = True
824             if duplicate:
825                 continue
826
827             self.options.append(VulkanEnum.Option(
828                 childName, childValue, childBitpos, childComment))
829
830
831 class VulkanFlags:
832
833     def __init__(self, rootNode):
834         self.name = rootNode.get('name')
835         self.type = rootNode.get('type')
836         self.enum = rootNode.get('requires')
837
838
839 class VulkanVariable:
840     def __init__(self, rootNode, constants, parentName):
841         self.name = rootNode.find('name').text
842         # Typename, dereferenced and converted to a useable C++ token
843         self.typeID = rootNode.find('type').text
844         self.baseType = self.typeID
845         self.childType = None
846         self.arrayLength = None
847
848         self.text = ''
849         for node in rootNode.itertext():
850             comment = rootNode.find('comment')
851             if comment is not None and comment.text == node:
852                 continue
853             self.text += node
854
855         typeMatch = re.search('.+?(?=' + self.name + ')', self.text)
856         self.type = typeMatch.string[typeMatch.start():typeMatch.end()]
857         self.type = ' '.join(self.type.split())
858         bracketMatch = re.search('(?<=\\[)[a-zA-Z0-9_]+(?=\\])', self.text)
859         if bracketMatch is not None:
860             matchText = bracketMatch.string[bracketMatch.start(
861             ):bracketMatch.end()]
862             self.childType = self.type
863             self.type += '[' + matchText + ']'
864             if matchText in constants:
865                 self.arrayLength = constants[matchText]
866             else:
867                 self.arrayLength = matchText
868
869         self.lengthMember = False
870         lengthString = rootNode.get('len')
871         lengths = []
872         if lengthString is not None:
873             lengths = re.split(',', lengthString)
874             lengths = list(filter(('null-terminated').__ne__, lengths))
875         assert(len(lengths) <= 1)
876         if self.arrayLength is None and len(lengths) > 0:
877             self.childType = '*'.join(self.type.split('*')[0:-1])
878             self.arrayLength = lengths[0]
879             self.lengthMember = True
880         if self.arrayLength is not None and self.arrayLength.startswith('latexmath'):
881             code = self.arrayLength[10:len(self.arrayLength)]
882             code = re.sub('\\[', '', code)
883             code = re.sub('\\]', '', code)
884             code = re.sub('\\\\(lceil|rceil)', '', code)
885             code = re.sub('{|}', '', code)
886             code = re.sub('\\\\mathit', '', code)
887             code = re.sub('\\\\over', '/', code)
888             code = re.sub('\\\\textrm', '', code)
889             self.arrayLength = code
890
891         # Dereference if necessary and handle members of variables
892         if self.arrayLength is not None:
893             self.arrayLength = re.sub('::', '->', self.arrayLength)
894             sections = self.arrayLength.split('->')
895             if sections[-1][0] == 'p' and sections[0][1].isupper():
896                 self.arrayLength = '*' + self.arrayLength
897
898
899 class VulkanStructure:
900     def __init__(self, name, rootNode, constants, extTypes):
901         self.name = name
902         self.members = []
903         self.guard = None
904         self.sTypeName = None
905         self.extendsStruct = rootNode.get('structextends')
906
907         for node in rootNode.findall('member'):
908             if(node.get('values') is not None):
909                 self.sTypeName = node.get('values')
910             self.members.append(VulkanVariable(
911                 node, constants, self.name))
912
913         for k, e in extTypes.items():
914             if k == self.name:
915                 if e.guard is not None:
916                     self.guard = e.guard
917
918
919 class VulkanExtension:
920     def __init__(self, rootNode):
921         self.name = rootNode.get('name')
922         self.number = int(rootNode.get('number'))
923         self.type = rootNode.get('type')
924         self.dependency = rootNode.get('requires')
925         self.guard = GetFeatureProtect(rootNode)
926         self.supported = rootNode.get('supported')
927         self.extNameStr = None
928         self.vktypes = []
929         self.vkfuncs = []
930         self.constants = OrderedDict()
931         self.enumValues = OrderedDict()
932         self.version = 0
933         self.node = rootNode
934
935         promotedto = rootNode.get('promotedto')
936         if promotedto != None:
937             # get last char of VK_VERSION_1_1 or VK_VERSION_1_2
938             minorVersion = promotedto[-1:]
939             if minorVersion.isdigit():
940                 self.version = minorVersion
941
942         for req in rootNode.findall('require'):
943             for ty in req.findall('type'):
944                 self.vktypes.append(ty.get('name'))
945
946             for func in req.findall('command'):
947                 self.vkfuncs.append(func.get('name'))
948
949             for enum in req.findall('enum'):
950                 base = enum.get('extends')
951                 name = enum.get('name')
952                 value = enum.get('value')
953                 bitpos = enum.get('bitpos')
954                 offset = enum.get('offset')
955                 # gets the VK_XXX_EXTENSION_NAME string
956                 if value == "\"" + self.name + "\"":
957                     self.extNameStr = name
958
959                 if value is None and bitpos is not None:
960                     value = 1 << int(bitpos)
961
962                 if offset is not None:
963                     offset = int(offset)
964                 if base is not None and offset is not None:
965                     enumValue = 1000000000 + 1000*(self.number - 1) + offset
966                     if enum.get('dir') == '-':
967                         enumValue = -enumValue
968                     self.enumValues[base] = (name, enumValue)
969                 else:
970                     self.constants[name] = value
971
972
973 class VulkanVersion:
974     def __init__(self, rootNode):
975         self.name = rootNode.get('name')
976         version_str = rootNode.get('number').split('.')
977         self.majorVersion = version_str[0]
978         self.minorVersion = version_str[1]
979         self.names = set()
980
981         for req in rootNode.findall('require'):
982             for ty in req.findall('type'):
983                 self.names.add(ty.get('name'))
984             for func in req.findall('command'):
985                 self.names.add(func.get('name'))
986             for enum in req.findall('enum'):
987                 self.names.add(enum.get('name'))
988         self.names = sorted(self.names)