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