• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright 2020-2023 The Khronos Group Inc.
4#
5# SPDX-License-Identifier: Apache-2.0
6
7# Description:
8# -----------
9# This script generates a .hpp file that can be included in an application
10# to generate json data that can then be used to generate the pipeline cache.
11
12import os
13import re
14from generator import (GeneratorOptions, OutputGenerator, noneStr,
15                       regSortFeatures, write)
16
17copyright = """
18/*
19 * Copyright (c) 2021 The Khronos Group Inc.
20 *
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the License.
23 * You may obtain a copy of the License at
24 *
25 *      http://www.apache.org/licenses/LICENSE-2.0
26 *
27 * Unless required by applicable law or agreed to in writing, software
28 * distributed under the License is distributed on an "AS IS" BASIS,
29 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 * See the License for the specific language governing permissions and
31 * limitations under the License.
32 *
33 *//*!
34 * \\file
35 * \\brief Defines JSON generators for Vulkan structures
36 */
37"""
38
39predefinedCode = """
40/********************************************************************************************/
41/** This code is generated. To make changes, please modify the scripts or the relevant xml **/
42/********************************************************************************************/
43
44#include <iostream>
45#include <map>
46#include <bitset>
47#include <functional>
48#include <sstream>
49#include <cassert>
50#include <cmath>
51#ifndef VULKAN_JSON_CTS
52    #include <vulkan/vulkan.h>
53#endif
54
55#ifdef _WIN32
56	#ifndef WIN32_LEAN_AND_MEAN
57	#define WIN32_LEAN_AND_MEAN
58	#endif
59	#define VC_EXTRALEAN
60	#define NOMINMAX
61	#include <windows.h>
62#endif
63
64namespace vk_json {
65
66static thread_local int s_num_spaces    = 0;
67static thread_local std::stringstream _string_stream;
68
69static void dumpPNextChain(const void* pNext);
70
71// By default, redirect to std::cout. Can stream it to a stringstream if needed.
72//#define   _OUT std::cout
73#define _OUT _string_stream
74
75// Helper utility to do indentation in the generated json file.
76#define PRINT_SPACE for (int k = 0; k < s_num_spaces; k++) _OUT << \" \";
77
78#define INDENT(sz) s_num_spaces += (sz);
79
80#define PRINT_VAL(c) PRINT_SPACE \\
81    if (s != "") {\\
82        _OUT << \"\\\"\" << s << \"\\\"\" << \" : \" << o << (c ? \",\" : \"\") << std::endl; \\
83    } else {\\
84        _OUT << o << (c ? \",\" : \"\") << std::endl; \\
85    }
86
87#define PRINT_STR(c) PRINT_SPACE \\
88    if (s != "") {\\
89        _OUT << \"\\\"\" << s << \"\\\"\" << \" : " << \"\\\"\" << o << \"\\\"\" << (c ? \",\" : \"\") << std::endl; \\
90    } else {\\
91        _OUT << \"\\\"\" << o << \"\\\"\" << (c ? \",\" : \"\") << std::endl; \\
92    }
93
94// To make sure the generated data is consistent across platforms,
95// we typecast to 32-bit and dump the data.
96// The value is not expected to exceed the range.
97static void print_size_t(const size_t* o, const std::string& s, bool commaNeeded=true)
98{
99    PRINT_SPACE
100    _OUT << \"\\\"\" << s << \"\\\"\" << \" : \" << static_cast<%s>(*o) << (commaNeeded ? \",\" : \"\") << std::endl;\\
101}
102static void print_size_t(size_t o, const std::string& s, bool commaNeeded=true)
103{
104    PRINT_SPACE
105    _OUT << \"\\\"\" << s << \"\\\"\" << \" : \" << static_cast<%s>(o) << (commaNeeded ? \",\" : \"\") << std::endl;\\
106}
107"""
108
109headerGuardTop = """#ifndef _VULKAN_JSON_DATA_HPP
110#define _VULKAN_JSON_DATA_HPP
111"""
112
113headerGuardBottom = """#endif // _VULKAN_JSON_DATA_HPP"""
114
115encodeBase64CodeCTS = """
116// Base 64 formatter class from executor/xeTestLogWriter.cpp
117
118class Base64Formatter
119{
120public:
121	const deUint8*	data;
122	int				numBytes;
123
124	Base64Formatter(const deUint8* data_, int numBytes_) : data(data_), numBytes(numBytes_) {}
125};
126
127std::ostream& operator<< (std::ostream& str, const Base64Formatter& fmt)
128{
129	static const char s_base64Table[64] =
130	{
131		'A','B','C','D','E','F','G','H','I','J','K','L','M',
132		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
133		'a','b','c','d','e','f','g','h','i','j','k','l','m',
134		'n','o','p','q','r','s','t','u','v','w','x','y','z',
135		'0','1','2','3','4','5','6','7','8','9','+','/'
136	};
137
138	const deUint8*	data = fmt.data;
139	int				numBytes = fmt.numBytes;
140	int				srcNdx = 0;
141
142	DE_ASSERT(data && (numBytes > 0));
143
144	/* Loop all input chars. */
145	while (srcNdx < numBytes)
146	{
147		int		numRead = de::min(3, numBytes - srcNdx);
148		deUint8	s0 = data[srcNdx];
149		deUint8	s1 = (numRead >= 2) ? data[srcNdx + 1] : 0;
150		deUint8	s2 = (numRead >= 3) ? data[srcNdx + 2] : 0;
151		char	d[4];
152
153		srcNdx += numRead;
154
155		d[0] = s_base64Table[s0 >> 2];
156		d[1] = s_base64Table[((s0 & 0x3) << 4) | (s1 >> 4)];
157		d[2] = s_base64Table[((s1 & 0xF) << 2) | (s2 >> 6)];
158		d[3] = s_base64Table[s2 & 0x3F];
159
160		if (numRead < 3) d[3] = '=';
161		if (numRead < 2) d[2] = '=';
162
163		/* Write data. */
164		str.write(&d[0], sizeof(d));
165	}
166
167	return str;
168}
169
170inline Base64Formatter toBase64(const deUint8* bytes, int numBytes) {return Base64Formatter(bytes, numBytes); }
171
172static void print_void_data(const void * o, int oSize, const std::string& s, bool commaNeeded=true)
173{
174	if (o != NULL && oSize != 0)
175	{
176		PRINT_SPACE _OUT << "\\\"" << s << "\\\"" << " : " << "\\\"" << toBase64((deUint8*)o, oSize) << "\\\"" << (commaNeeded ? "," : "") << std::endl;
177	}
178	else
179	{
180		PRINT_SPACE _OUT << "\\\"" << s << "\\\"" << " : " << "\\\"NULL\\\"" << (commaNeeded ? "," : "") << std::endl;
181	}
182}
183"""
184encodeBase64Code = """
185// Base 64 formatter class from executor/xeTestLogWriter.cpp
186
187class Base64Formatter
188{
189public:
190	const uint8_t*	data;
191	int				numBytes;
192
193	Base64Formatter(const uint8_t* data_, int numBytes_) : data(data_), numBytes(numBytes_) {}
194};
195
196std::ostream& operator<< (std::ostream& str, const Base64Formatter& fmt)
197{
198	static const char s_base64Table[64] =
199	{
200		'A','B','C','D','E','F','G','H','I','J','K','L','M',
201		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
202		'a','b','c','d','e','f','g','h','i','j','k','l','m',
203		'n','o','p','q','r','s','t','u','v','w','x','y','z',
204		'0','1','2','3','4','5','6','7','8','9','+','/'
205	};
206
207	const uint8_t*	data = fmt.data;
208	int				numBytes = fmt.numBytes;
209	int				srcNdx = 0;
210
211	assert(data && (numBytes > 0));
212
213	/* Loop all input chars. */
214	while (srcNdx < numBytes)
215	{
216        #undef min
217		int		numRead = std::min(3, numBytes - srcNdx);
218		uint8_t	s0 = data[srcNdx];
219		uint8_t	s1 = (numRead >= 2) ? data[srcNdx + 1] : 0;
220		uint8_t	s2 = (numRead >= 3) ? data[srcNdx + 2] : 0;
221		char	d[4];
222
223		srcNdx += numRead;
224
225		d[0] = s_base64Table[s0 >> 2];
226		d[1] = s_base64Table[((s0 & 0x3) << 4) | (s1 >> 4)];
227		d[2] = s_base64Table[((s1 & 0xF) << 2) | (s2 >> 6)];
228		d[3] = s_base64Table[s2 & 0x3F];
229
230		if (numRead < 3) d[3] = '=';
231		if (numRead < 2) d[2] = '=';
232
233		/* Write data. */
234		str.write(&d[0], sizeof(d));
235	}
236
237	return str;
238}
239
240inline Base64Formatter toBase64(const uint8_t* bytes, int numBytes) {return Base64Formatter(bytes, numBytes); }
241
242static void print_void_data(const void * o, int oSize, const std::string& s, bool commaNeeded=true)
243{
244	if (o != NULL && oSize != 0)
245	{
246		PRINT_SPACE _OUT << "\\\"" << s << "\\\"" << " : " << "\\\"" << toBase64((uint8_t*)o, oSize) << "\\\"" << (commaNeeded ? "," : "") << std::endl;
247	}
248	else
249	{
250		PRINT_SPACE _OUT << "\\\"" << s << "\\\"" << " : " << "\\\"NULL\\\"" << (commaNeeded ? "," : "") << std::endl;
251	}
252}
253"""
254
255class JSONGeneratorOptions(GeneratorOptions):
256    """JSONGeneratorOptions - subclass of GeneratorOptions.
257
258    Adds options used by JSONOutputGenerator objects during C language header
259    generation."""
260
261    def __init__(self,
262                 prefixText="",
263                 genFuncPointers=True,
264                 protectFile=True,
265                 protectFeature=True,
266                 protectProto=None,
267                 protectProtoStr=None,
268                 apicall='',
269                 apientry='',
270                 apientryp='',
271                 isCTS = False,
272                 indentFuncProto=True,
273                 indentFuncPointer=False,
274                 alignFuncParam=0,
275                 genEnumBeginEndRange=False,
276                 genAliasMacro=False,
277                 aliasMacro='',
278                 vulkanLayer=False,
279                 **kwargs
280                 ):
281
282        GeneratorOptions.__init__(self, **kwargs)
283        self.isCTS = isCTS
284
285        self.vulkanLayer = vulkanLayer
286
287class JSONOutputGenerator(OutputGenerator):
288    # This is an ordered list of sections in the header file.
289    TYPE_SECTIONS = ['basetype', 'handle', 'enum',
290                     'group', 'bitmask', 'struct']
291    ALL_SECTIONS = TYPE_SECTIONS
292
293    def __init__(self, *args, **kwargs):
294        super().__init__(*args, **kwargs)
295        # Internal state - accumulators for different inner block text
296        self.sections = {section: [] for section in self.ALL_SECTIONS}
297        self.feature_not_empty = False
298        self.may_alias         = None
299        self.vkscFeatureList   = []
300        self.vkFeatureLayerList = []
301
302        # Fills in some extensions for exclusion while generating code for layer.
303        self.vkLayerNotReqList = set([""])
304
305        self.platformList      = ["xlib",
306                                  "xlib_xrandr",
307                                  "xcb",
308                                  "wayland",
309                                  "directfb",
310                                  "android",
311                                  "win32",
312                                  "vi",
313                                  "ios",
314                                  "macos",
315                                  "metal",
316                                  "fuchsia",
317                                  "ggp",
318                                  "QNX",
319                                  "provisional"]
320        self.baseTypeList      = ["int32_t",
321                                  "uint32_t",
322                                  "uint8_t",
323                                  "uint64_t",
324                                  "float",
325                                  "int",
326                                  "double",
327                                  "int64_t",
328                                  "uint16_t",
329                                  "char"]
330
331    def printBaseTypes(self):
332        for baseType in self.baseTypeList:
333            printStr = "    PRINT_VAL(commaNeeded)\n"
334
335            # Some special handling needed here.
336            if baseType == 'char':
337                write("static void print_%s(const %s * const* o, const std::string& s, bool commaNeeded=true)\n" %(baseType, self.baseTypeListMap[baseType]) +
338                  "{\n"                                                                                                           						+
339                  "    PRINT_STR(commaNeeded)\n"                                                                                  						+
340                  "}\n"
341                  , file=self.outFile
342                 )
343
344            if self.isCTS and baseType == "float":
345                printStr = "	if (std::isnan(o))\n"
346                printStr +="	{\n"
347                printStr +="		PRINT_SPACE\n"
348                printStr +="		if (s != \"\")\n"
349                printStr +="			_OUT << \"\\\"\" << s << \"\\\"\" << \" : \\\"NaN\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
350                printStr +="		else\n"
351                printStr +="			_OUT << \"\\\"NaN\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
352                printStr +="	}\n"
353                printStr +="	else\n"
354                printStr +="	{\n"
355                printStr +="		PRINT_VAL(commaNeeded)\n"
356                printStr +="	}\n"
357
358            write("static void print_%s(%s o, const std::string& s, bool commaNeeded=true)\n" %(baseType, self.baseTypeListMap[baseType]) +
359                  "{\n"                                                                                        						 +
360                  printStr                                                                                     						 +
361                  "}\n"
362                  , file=self.outFile
363                  )
364
365            if baseType == 'char':
366                printStr = "    PRINT_STR(commaNeeded)\n"
367
368            if self.isCTS and baseType == "float":
369                printStr = "	if (std::isnan(*o))\n"
370                printStr +="	{\n"
371                printStr +="		PRINT_SPACE\n"
372                printStr +="		if (s != \"\")\n"
373                printStr +="			_OUT << \"\\\"\" << s << \"\\\"\" << \" : \\\"NaN\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
374                printStr +="		else\n"
375                printStr +="			_OUT << \"\\\"NaN\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
376                printStr +="	}\n"
377                printStr +="	else\n"
378                printStr +="	{\n"
379                printStr +="		PRINT_VAL(commaNeeded)\n"
380                printStr +="	}\n"
381
382            write("static void print_%s(const %s * o, const std::string& s, bool commaNeeded=true)\n" %(baseType, self.baseTypeListMap[baseType]) +
383                  "{\n"                                                                                                						 +
384                  printStr                                                                                             						 +
385                  "}\n"
386                  , file=self.outFile
387                 )
388
389    def createLayerUnusedList(self):
390        allExtensions = self.registry.reg.findall('extensions')
391        for extensions in allExtensions:
392            extensionList = extensions.findall("extension")
393            for extension in extensionList:
394                for platform in self.platformList:
395                    if re.search(platform, extension.get("name"), re.IGNORECASE):
396                        requiredList = extension.findall("require")
397                        for requiredItem in requiredList:
398                            typeList = requiredItem.findall("type")
399                            for typeName in typeList:
400                                if platform == "vi":
401                                    if re.search("NN", extension.get("name")):
402                                        self.vkLayerNotReqList.add(typeName.get("name"))
403                                else:
404                                    self.vkLayerNotReqList.add(typeName.get("name"))
405                        break
406
407        typesList = self.registry.reg.findall('types')
408        for types in typesList:
409            typeList = types.findall("type")
410            for type in typeList:
411                if type.get("name") != "":
412                    cat  = type.get("category")
413                    name = type.get("name")
414                    if cat in {"handle", "bitmask", "basetype", "enum", "struct"}:
415                        for platform in self.platformList:
416                            if re.search(platform, name, re.IGNORECASE):
417                                if platform == "vi":
418                                    if re.search("NN", name):
419                                        self.vkLayerNotReqList.add(name)
420                                else:
421                                    self.vkLayerNotReqList.add(name)
422                                break
423
424
425    def createvkscFeatureList(self):
426        for feature in self.registry.reg.findall('feature'):
427            if feature.get('api').find('vulkansc') != -1:
428                # Remove entries that are removed in features in VKSC profile.
429                requiredList = feature.findall("require")
430
431                for requiredItem in requiredList:
432                    typeList = requiredItem.findall("type")
433                    for typeName in typeList:
434                        if typeName.get("name") != "":
435                            self.vkscFeatureList.append(typeName.get("name"))
436
437                removeItemList = feature.findall("remove")
438                for removeItem in removeItemList:
439                    removeTypes = removeItem.findall("type")
440                    for item in removeTypes:
441                        if self.vkscFeatureList.count(item.get("name")) > 0:
442                            self.vkscFeatureList.remove(item.get("name"))
443
444        allExtensions = self.registry.reg.findall('extensions')
445        for extensions in allExtensions:
446            extensionList = extensions.findall("extension")
447            for extension in extensionList:
448                if extension.get("supported").find("vulkansc") != -1:
449                    requiredList = extension.findall("require")
450                    for requiredItem in requiredList:
451                        typeList = requiredItem.findall("type")
452                        for typeName in typeList:
453                            self.vkscFeatureList.append(typeName.get("name"))
454
455    def printPrototypesAndExtensionDump(self):
456        code = ""
457
458        code += "/*************************************** Begin prototypes ***********************************/\n"
459        if self.vulkanLayer:
460            typesList = self.registry.reg.findall('types')
461            for types in typesList:
462                typeList = types.findall("type")
463                for type in typeList:
464                    if type.get("name") != "":
465                        cat  = type.get("category")
466                        name = type.get("name")
467
468            enumList = self.registry.reg.findall('enums')
469            for enums in enumList:
470                name = enums.get("name")
471        else:
472            typesList = self.registry.reg.findall('types')
473            for types in typesList:
474                typeList = types.findall("type")
475                for type in typeList:
476                    if type.get("name") != "":
477                        cat  = type.get("category")
478                        name = type.get("name")
479
480        code += "/*************************************** End prototypes ***********************************/\n\n"
481        code += "static void dumpPNextChain(const void* pNext) {\n"
482        code += "      VkBaseInStructure *pBase = (VkBaseInStructure*)pNext;\n"
483        code += "      if (pNext) {\n"
484        code += "           PRINT_SPACE\n"
485        code += "           _OUT << \"\\\"pNext\\\":\"<< std::endl;\n\n"
486        code += "          switch (pBase->sType) {\n"
487
488        for type in typeList:
489            if type.get('category') == 'struct' and type.get('structextends') is not None:
490                if (self.vulkanLayer and (type.get('name') not in self.vkLayerNotReqList)) or (not self.vulkanLayer):
491                    members = type.findall('member')
492                    for m in members:
493                            n = type.get('name')
494                            if m.get('values') and (n in self.vkFeatureLayerList):
495                                code += "             case %s:" %(m.get('values'))
496                                code += "print_%s((%s *) pNext, \"%s\", true);\n" %(n, n, n)
497                                code += "             break;\n"
498
499        code += "             default: assert(false); // No structure type matching\n"
500        code += "         }\n"
501        code += "     }\n"
502        code += "  }\n"
503
504        return code
505
506
507    def beginFile(self, genOpts):
508        OutputGenerator.beginFile(self, genOpts)
509
510        self.vulkanLayer = genOpts.vulkanLayer
511        if self.vulkanLayer:
512            self.createLayerUnusedList()
513
514        self.createvkscFeatureList()
515
516        self.isCTS = genOpts.isCTS
517
518        self.baseTypeListMap  = {
519                                  "int32_t"   : "deInt32" if self.isCTS else "int32_t",
520                                  "uint32_t"  : "deUint32" if self.isCTS else "uint32_t",
521                                  "uint8_t"   : "deUint8" if self.isCTS else "uint8_t",
522                                  "uint64_t"  : "deUint64" if self.isCTS else "uint64_t",
523                                  "float"     : "float",
524                                  "int"       : "int",
525                                  "double"    : "double",
526                                  "int64_t"   : "deInt64" if self.isCTS else "int64_t",
527                                  "uint16_t"  : "deUint16" if self.isCTS else "uint16_t",
528                                  "char"      : "char"
529                                }
530
531        write(headerGuardTop, file=self.outFile, end='')
532        write(copyright, file=self.outFile)
533        if self.isCTS:
534            write(predefinedCode % ("deUint32", "deUint32"), file=self.outFile)
535        else:
536            write(predefinedCode % ("uint32_t", "uint32_t"), file=self.outFile)
537        self.printBaseTypes()
538        if self.isCTS:
539            write(encodeBase64CodeCTS, file=self.outFile)
540        else:
541            write(encodeBase64Code, file=self.outFile)
542
543    def endFile(self):
544        write(self.printPrototypesAndExtensionDump(), file=self.outFile)
545        write("}//End of namespace vk_json\n", file=self.outFile) # end of namespace
546        write(headerGuardBottom, file=self.outFile, end='') # end of _VULKAN_JSON_DATA_HPP
547        OutputGenerator.endFile(self)
548
549    def beginFeature(self, interface, emit):
550        OutputGenerator.beginFeature(self, interface, emit)
551        self.sections = {section: [] for section in self.ALL_SECTIONS}
552        self.feature_not_empty = False
553
554    def endFeature(self):
555        if self.emit:
556            if self.feature_not_empty:
557                if self.genOpts.conventions.writeFeature(self.featureExtraProtect, self.genOpts.filename):
558
559                    for section in self.TYPE_SECTIONS:
560                        contents = self.sections[section]
561                        if contents:
562                            write('\n'.join(contents), file=self.outFile)
563
564        # Finish processing in superclass
565        OutputGenerator.endFeature(self)
566
567    def appendSection(self, section, text):
568        self.sections[section].append(text)
569        self.feature_not_empty = True
570
571    def genEnumData(self, name, obj):
572        code = ""
573        code += "     if (str != \"\") _OUT << \"\\\"\" << str << \"\\\"\" << \" : \";\n"
574        code += "     if (commaNeeded)\n"
575        code += "         _OUT << \"\\\"\" <<  %s_map[%sobj] << \"\\\",\" << std::endl;\n" %(name, obj)
576        code += "     else\n"
577        code += "         _OUT << \"\\\"\" << %s_map[%sobj] << \"\\\"\" << std::endl;\n" %(name, obj)
578        return code
579
580    def genEnumCode(self, name):
581        code = ""
582        code += "static void print_%s(%s obj, const std::string& str, bool commaNeeded=true) {\n" %(name, name)
583        code += "     PRINT_SPACE\n"
584        code += self.genEnumData(name, "")
585        code += "}\n"
586
587        code += "static void print_%s(const %s * obj, const std::string& str, bool commaNeeded=true) {\n" %(name, name)
588        code += "     PRINT_SPACE\n"
589        code += self.genEnumData(name, "*")
590        code += "}\n"
591
592        return code
593
594    def genBasetypeCode(self, str1, str2, name):
595        code = ""
596        code += "static void print_" + name + "(" + str1 + name + str2 + " const std::string& str, bool commaNeeded=true) {\n"
597        code += "     PRINT_SPACE\n"
598        if name == "VkBool32":
599            code += "     _OUT << \"\\\"\" << str << \"\\\"\" << \" : \" << \"\\\"\" << ((obj == 0) ? (\"VK_FALSE\") : (\"VK_TRUE\")) << \"\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
600        else:
601            code += "     _OUT << \"\\\"\" << str << \"\\\"\" << \" : \" << \"\\\"\" << obj << \"\\\"\" << (commaNeeded ? \",\" : \"\") << std::endl;\n"
602        code += "}\n"
603        return code
604
605    def genHandleCode(self, str1, str2, name):
606        code = ""
607        code += "static void print_%s(%s%s%s const std::string& str, bool commaNeeded=true) {\n" %(name, str1, name, str2)
608        code += "     PRINT_SPACE\n"
609        code += "     if (commaNeeded)\n"
610        code += "         _OUT << \"\\\"\" << str << \"\\\"\" << \",\" << std::endl;\n"
611        code += "     else\n"
612        code += "         _OUT << \"\\\"\" << str << \"\\\"\" << std::endl;\n"
613        code += "}\n"
614        return code
615
616    def genBitmaskCode(self, str1, str2, name, mapName):
617        if mapName is not None:
618            code = ""
619            code += "static void print_%s(%s%s%s const std::string& str, bool commaNeeded=true) {\n" %(name, str1, name, str2)
620            code += "     PRINT_SPACE\n"
621            code += "     if (str != \"\") _OUT << \"\\\"\" << str << \"\\\"\" << \" : \";\n"
622            code += "     const int max_bits = 64; // We don't expect the number to be larger.\n"
623            code += "     std::bitset<max_bits> b(obj);\n"
624            code += "     _OUT << " + "\"\\\"\"" + ";\n"
625            code += "     if (obj == 0) _OUT << \"0\";\n"
626            code += "     for (unsigned int i = 0, bitCount = 0; i < b.size(); i++) {\n"
627            code += "         if (b[i] == 1) {\n"
628            code += "             bitCount++;\n"
629            code += "             if (bitCount < b.count())\n"
630            code += "                 _OUT << %s_map[1ULL<<i] << \" | \";\n" %(mapName)
631            code += "             else\n"
632            code += "                 _OUT << %s_map[1ULL<<i];\n" %(mapName)
633            code += "         }\n"
634            code += "     }\n"
635            code += "     if (commaNeeded)\n"
636            code += "       _OUT << \"\\\"\" << \",\";\n"
637            code += "     else\n"
638            code += "       _OUT << \"\\\"\"<< \"\";\n"
639            code += "     _OUT << std::endl;\n"
640            code += "}\n"
641
642        else:
643            code = ""
644            code += "static void print_%s(%s%s%s const std::string& str, bool commaNeeded=true) {\n" %(name, str1, name, str2)
645            code += "     PRINT_SPACE\n"
646            code += "     if (commaNeeded)\n"
647            code += "         _OUT << \"\\\"\" << str << \"\\\"\" << \" : \" << obj << \",\" << std::endl;\n"
648            code += "     else\n"
649            code += "         _OUT << \"\\\"\" << str << \"\\\"\" << \" : \" << obj << std::endl;\n"
650            code += "}\n"
651
652        return code
653
654    def genType(self, typeinfo, name, alias):
655        OutputGenerator.genType(self, typeinfo, name, alias)
656        typeElem = typeinfo.elem
657        body = ""
658
659        self.vkFeatureLayerList.append(name);
660
661        category = typeElem.get('category')
662        if category == 'funcpointer':
663            section = 'struct'
664        else:
665            section = category
666
667        if category in ('struct', 'union'):
668            self.genStruct(typeinfo, name, alias)
669        else:
670            if typeElem.get('category') == 'bitmask':
671                for elem in typeElem:
672                    if elem.tag == 'name':
673                        body += self.genBitmaskCode("", " obj,", elem.text, typeElem.get('requires'))
674                        body += self.genBitmaskCode("const ", " * obj,", elem.text, typeElem.get('requires'))
675
676            elif typeElem.get('category') == 'basetype':
677                    for elem in typeElem:
678                        if elem.tag == 'name':
679                            body += self.genBasetypeCode("", " obj,", elem.text)
680                            body += self.genBasetypeCode("const ", " * obj,", elem.text)
681
682            elif typeElem.get('category') == 'handle':
683                    for elem in typeElem:
684                        if elem.tag == 'name':
685                            body += self.genHandleCode("", " obj,", elem.text)
686                            body += self.genHandleCode("const ", " * obj,", elem.text)
687            if body:
688                self.appendSection(section, body)
689
690    def paramIsStruct(self, memberType):
691        if str(self.getTypeCategory(memberType)) == 'struct':
692            return 1
693        return 0
694
695    # Helper taken from the validation layers code.
696    def paramIsPointer(self, param):
697        ispointer = False
698        for elem in param:
699            if elem.tag == 'type' and elem.tail is not None and '*' in elem.tail:
700                ispointer = True
701        return ispointer
702
703    # Helper taken from the validation layers code.
704    def paramIsStaticArray(self, param):
705        isstaticarray = 0
706        paramname = param.find('name')
707        if (paramname.tail is not None) and ('[' in paramname.tail) and (']' in paramname.tail):
708            isstaticarray = paramname.tail.count('[')
709            if isstaticarray:
710                arraySize = paramname.tail[1]
711
712        if isstaticarray:
713            return arraySize
714        else:
715            return 0
716
717    def generateStructMembercode(self, param, str1, str2, str3, str4, memberName, typeName, isCommaNeeded):
718        length = ""
719        code = ""
720        isArr = param.get('len') is not None
721
722        if param.get('len') is not None:
723            length = str2 + param.get('len') + ")"
724        length = length.replace(')', '')
725        length = length.replace(',1', '')
726
727        code += "     PRINT_SPACE\n"
728        code += "     _OUT << \"\\\"%s\\\": \" << std::endl;\n" %(memberName)
729
730        if self.paramIsPointer(param): code += str4 + memberName + ") {\n"
731        else:                          code += "     {\n"
732
733        # TODO: With some tweak, we can use the genArrayCode() here.
734        if isArr is True:
735            code += "         PRINT_SPACE\n"
736            code += "         _OUT << \"[\" << std::endl;\n"
737            code += "         for (unsigned int i = 0; i < %s; i++) {\n" %(length)
738            code += "           if (i+1 == %s)\n" %(length)
739            code += "               print_%s(%s%s[i], \"%s\", 0);\n" %(typeName, str2, memberName, memberName)
740            code += "           else\n"
741            code += "               print_%s(%s%s[i], \"%s\", 1);\n" %(typeName, str2, memberName, memberName)
742            code += "         }\n"
743            code += "         PRINT_SPACE\n"
744            if isCommaNeeded:
745                code += "         _OUT << \"],\" << std::endl;\n"
746            else:
747                code += "         _OUT << \"]\" << std::endl;\n"
748            code += "    }\n"
749        else:
750            if (typeName == "VkAccelerationStructureGeometryKHR"):
751                code += "           print_%s(*%s%s, \"%s\", %s);\n" %(typeName, str2, memberName, memberName, str(isCommaNeeded))
752            else:
753                code += "           print_%s(%s%s, \"%s\", %s);\n" %(typeName, str2, memberName, memberName, str(isCommaNeeded))
754            code += "     }\n"
755
756        if self.paramIsPointer(param):
757            code += "     else\n"
758            code += "     {\n"
759            if isCommaNeeded:
760                code += "         PRINT_SPACE _OUT << \"\\\"NULL\\\"\"<< \",\"<< std::endl;\n"
761            else:
762                code += "         PRINT_SPACE _OUT << \"\\\"NULL\\\"\"<< \"\"<< std::endl;\n"
763            code += "     }\n"
764
765        return code
766
767    def genPNextCode(self, str2):
768        code  = ""
769        code += "      if (%spNext) {\n" %(str2)
770        code += "         dumpPNextChain(%spNext);\n" %(str2)
771        code += "      } else {\n"
772        code += "         PRINT_SPACE\n"
773        code += "         _OUT << \"\\\"pNext\\\":\" << \"\\\"NULL\\\"\"<< \",\"<< std::endl;\n"
774        code += "     }\n"
775
776        return code
777
778    # Prints out member name followed by empty string.
779    def genEmptyCode(self, memberName, str2, isCommaNeeded):
780        code = ""
781        if not self.isCTS:
782            code +=  "     /** Note: printing just an empty entry here **/\n"
783        else:
784            code +=  "     // CTS : required value\n"
785        code +=  "     PRINT_SPACE"
786        if isCommaNeeded:
787            if self.isCTS and (memberName == "module" or memberName == "layout" or memberName == "renderPass" or memberName == "conversion"):
788                code +=  "    _OUT << \"\\\"\" << \"%s\" << \"\\\"\" << \" : \" << %s%s.getInternal() << \",\" << std::endl;\n" %(memberName, str2, memberName)
789            else:
790                code +=  "    _OUT << \"\\\"\" << \"%s\" << \"\\\"\" << \" : \" << \"\\\"\" << \"\\\",\" << std::endl;\n" %(memberName)
791        else:
792            if self.isCTS and (memberName == "module" or memberName == "layout" or memberName == "renderPass" or memberName == "conversion"):
793                code +=  "    _OUT << \"\\\"\" << \"%s\" << \"\\\"\" << \" : \" << %s%s.getInternal() << std::endl;\n" %(memberName, str2, memberName)
794            else:
795                code +=  "    _OUT << \"\\\"\" << \"%s\" << \"\\\"\" << \" : \" << \"\\\"\" << \"\\\"\" << std::endl;\n" %(memberName)
796        return code
797
798    def genArrayCode(self, structName, name, typeName, str2, arraySize, needStrPrint, isArrayType, isCommaNeeded):
799            comma = "," if isCommaNeeded else ""
800            code = ""
801            arraySize = arraySize.replace(')', '')
802            needsTmp = needStrPrint or (str(self.getTypeCategory(typeName)) == 'handle')
803
804            if needStrPrint: printStr = "tmp.str()"
805            else:            printStr = "\"\""
806
807            code += "     PRINT_SPACE\n"
808            code += "     _OUT << \"\\\"%s\\\":\" << std::endl;\n" %(name)
809            code += "     PRINT_SPACE\n"
810            if not isArrayType:
811                code += "     if (%s%s) {\n" %(str2, name)
812            code += "       _OUT << \"[\" << std::endl;\n"
813            code += "       for (unsigned int i = 0; i < %s; i++) {\n" %(arraySize)
814            if self.isCTS and (structName == "VkPipelineLayoutCreateInfo" or structName == "VkDescriptorSetLayoutBinding"):
815                code += "           bool isCommaNeeded = (i+1) != %s;\n" %(arraySize)
816                code += "           if (isCommaNeeded)\n"
817                code += "           {\n"
818                code += "               PRINT_SPACE\n"
819                code += "               _OUT << %s%s[i].getInternal() << \",\" << std::endl;\n" %(str2, name)
820                code += "           }\n"
821                code += "           else\n"
822                code += "           {\n"
823                code += "               PRINT_SPACE\n"
824                code += "               _OUT << %s%s[i].getInternal() << std::endl;\n" %(str2, name)
825                code += "           }\n"
826            else:
827                if needsTmp:
828                    code += "           std:: stringstream tmp;\n"
829
830                    # Special case handling for giving unique names for pImmutableSamplers if there are multiple
831                    # bindings in the same Descriptor set layout.
832                    if name == "pImmutableSamplers":
833                        code += "           tmp << \"%s\" << \"_\" << (%sbinding) << \"_\" << i;\n" %(name, str2)
834                    else:
835                        code += "           tmp << \"%s\" << \"_\" << i;\n" %(name)
836
837                code += "           bool isCommaNeeded = (i+1) != %s;\n" %(arraySize)
838
839                if str(self.getTypeCategory(typeName)) == 'handle':
840                    code += "           print_%s(%s%s[i], tmp.str(), isCommaNeeded);\n" %(typeName, str2, name)
841                else:
842                    if self.isCTS and name == "pipelineIdentifier":
843                        code += "           print_uint32_t((%s)%s%s[i], %s, isCommaNeeded);\n" %(self.baseTypeListMap["uint32_t"], str2, name, printStr)
844                    else:
845                        code += "           print_%s(%s%s[i], %s, isCommaNeeded);\n" %(typeName, str2, name, printStr)
846            code += "       }\n"
847            code += "       PRINT_SPACE\n"
848            code += "       _OUT << \"]\" << \"%s\" << std::endl;\n" %(comma)
849            if not isArrayType == True:
850                code += "     } else {\n"
851                code += "       _OUT << \"\\\"NULL\\\"\" << \"%s\" << std::endl;\n" %(comma)
852                code += "     }\n"
853            return code
854
855    def genStructCode(self, param, str1, str2, str3, str4, structName, isCommaNeeded):
856        code = ""
857        memberName = ""
858        typeName = ""
859
860        for elem in param:
861            if elem.text.find('PFN_') != -1:
862                return "     /** Note: Ignoring function pointer (%s). **/\n" %(elem.text)
863
864            if elem.text == 'pNext':
865                return self.genPNextCode(str2)
866
867            if elem.tag == 'name':
868                memberName = elem.text
869
870            if elem.tag == 'type':
871                typeName = elem.text
872
873            # Some arrays have constant sizes.
874            if elem.text.find("VK_") != -1:
875                return self.genArrayCode(structName, memberName, typeName, str2, elem.text, False, True, isCommaNeeded)
876
877        if self.paramIsStaticArray(param):
878            return self.genArrayCode(structName, memberName, typeName, str2, self.paramIsStaticArray(param), False, True, isCommaNeeded)
879
880        # If the struct's member is another struct, we need a different way to handle.
881        elif self.paramIsStruct(typeName) == 1:
882            code += self.generateStructMembercode(param, str1, str2, str3, str4, memberName, typeName, isCommaNeeded)
883
884        # Ignore void* data members
885        elif self.paramIsPointer(param) and typeName == 'void':
886            if structName == "VkSpecializationInfo":
887                    return "     print_void_data(%s%s, int(%sdataSize), \"%s\", 0);\n" %(str2, memberName, str2, memberName)
888            if self.isCTS:
889                if structName == "VkPipelineCacheCreateInfo":
890                    return "     print_void_data(%s%s, int(%sinitialDataSize), \"%s\", 0);\n" %(str2, memberName, str2, memberName)
891            return "     /** Note: Ignoring void* data. **/\n"
892
893        # For pointers where we have the 'len' field, dump them as arrays.
894        elif self.paramIsPointer(param) and param.get('len') is not None and param.get('len').find('null-terminated') == -1 and param.get('len').find('latexmath') == -1:
895            if memberName == "versionData":
896                return self.genArrayCode(structName, memberName, typeName, str2, param.get('len')+")", False, False, isCommaNeeded)
897            else:
898                return self.genArrayCode(structName, memberName, typeName, str2, str2+param.get('len')+")", False, False, isCommaNeeded)
899
900        # Special handling for VkPipelineMultisampleStateCreateInfo::pSampleMask
901        elif typeName in "VkSampleMask":
902            code += "     %s sampleMaskSize = ((%srasterizationSamples + 31) / 32);\n" % (self.baseTypeListMap["uint32_t"], str2)
903            code += self.genArrayCode(structName, memberName, "uint32_t", str2, "sampleMaskSize", False, False, isCommaNeeded)
904            return code
905
906        # If a struct member is just a handle.
907        elif str(self.getTypeCategory(typeName)) == 'handle':
908            return self.genEmptyCode(memberName, str2, isCommaNeeded)
909
910        else:
911            code += "     print_%s(%s%s, \"%s\", %s);\n" %(typeName, str2, memberName, memberName, str(isCommaNeeded))
912
913        return code
914
915    def genStruct(self, typeinfo, typeName, alias):
916        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
917        body = ""
918        typeElem = typeinfo.elem
919
920        if alias:
921            body = 'typedef ' + alias + ' ' + typeName + ';\n'
922        else:
923            genStr1 = [""   , "const "]
924            genStr2 = ["obj.", "obj->" ]
925            genStr3 = [" obj, const std::string& s, bool commaNeeded=true) {" , " * obj, const std::string& s, bool commaNeeded=true) {"]
926            genStr4 = ["     if (obj.", "     if (obj->"]
927
928            for index in range(len(genStr1)):
929                body += "static void print_%s(%s%s%s\n" %(typeName, genStr1[index], typeName, genStr3[index])
930                body += "     PRINT_SPACE\n"
931                body += "     _OUT << \"{\" << std::endl;\n"
932                body += "     INDENT(4);\n"
933                body += "\n"
934                count = 0
935                numMembers = len(typeElem.findall('.//member'))
936
937                isCommaNeeded = 1
938                for member in typeElem.findall('.//member'):
939                    count = count + 1
940                    if count == numMembers:
941                        isCommaNeeded = 0
942
943                    body += self.genStructCode(member, genStr1[index], genStr2[index], genStr3[index], genStr4[index], typeName, isCommaNeeded)
944                    body += "\n"
945
946                body += "     INDENT(-4);\n"
947                body += "     PRINT_SPACE\n"
948                body += "     if (commaNeeded)\n"
949                body += "         _OUT << \"},\" << std::endl;\n"
950                body += "     else\n"
951                body += "         _OUT << \"}\" << std::endl;\n"
952                body += "}\n"
953
954        self.appendSection('struct', body)
955
956    def genGroup(self, groupinfo, groupName, alias=None):
957        OutputGenerator.genGroup(self, groupinfo, groupName, alias)
958        groupElem = groupinfo.elem
959        body = ""
960        section = 'enum'
961
962        body += "static std::map<%s, std::string> %s_map = {\n" %(self.baseTypeListMap["uint64_t"], groupName)
963        enums = groupElem.findall('enum')
964
965        for enum in enums:
966            if enum.get('value'):
967                body += "    std::make_pair(%s, \"%s\"),\n" %(enum.get('value'), enum.get('name'))
968
969            elif enum.get('bitpos'):
970                body += "    std::make_pair(1ULL << %s, \"%s\"),\n" %(enum.get('bitpos'), enum.get('name'))
971
972            #TODO: Some enums have no offset. How to handle those?
973            elif enum.get('extends') and enum.get("extnumber") and enum.get("offset"):
974                extNumber = int(enum.get("extnumber"))
975                offset = int(enum.get("offset"))
976                enumVal = self.extBase + (extNumber - 1) * self.extBlockSize + offset
977                body += "    std::make_pair(%s, \"%s\"),\n" %(str(enumVal), enum.get('name'))
978
979        body += "};\n"
980        body += self.genEnumCode(groupName)
981
982        self.appendSection(section, body)
983