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