• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2017 The Khronos Group Inc.
4# Copyright (c) 2015-2017 Valve Corporation
5# Copyright (c) 2015-2017 LunarG, Inc.
6# Copyright (c) 2015-2017 Google Inc.
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12#     http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20# Author: Mark Lobodzinski <mark@lunarg.com>
21# Author: Tobin Ehlis <tobine@google.com>
22# Author: John Zulauf <jzulauf@lunarg.com>
23
24import os,re,sys
25import xml.etree.ElementTree as etree
26from generator import *
27from collections import namedtuple
28from common_codegen import *
29
30#
31# HelperFileOutputGeneratorOptions - subclass of GeneratorOptions.
32class HelperFileOutputGeneratorOptions(GeneratorOptions):
33    def __init__(self,
34                 filename = None,
35                 directory = '.',
36                 apiname = None,
37                 profile = None,
38                 versions = '.*',
39                 emitversions = '.*',
40                 defaultExtensions = None,
41                 addExtensions = None,
42                 removeExtensions = None,
43                 emitExtensions = None,
44                 sortProcedure = regSortFeatures,
45                 prefixText = "",
46                 genFuncPointers = True,
47                 protectFile = True,
48                 protectFeature = True,
49                 apicall = '',
50                 apientry = '',
51                 apientryp = '',
52                 alignFuncParam = 0,
53                 library_name = '',
54                 expandEnumerants = True,
55                 helper_file_type = ''):
56        GeneratorOptions.__init__(self, filename, directory, apiname, profile,
57                                  versions, emitversions, defaultExtensions,
58                                  addExtensions, removeExtensions, emitExtensions, sortProcedure)
59        self.prefixText       = prefixText
60        self.genFuncPointers  = genFuncPointers
61        self.protectFile      = protectFile
62        self.protectFeature   = protectFeature
63        self.apicall          = apicall
64        self.apientry         = apientry
65        self.apientryp        = apientryp
66        self.alignFuncParam   = alignFuncParam
67        self.library_name     = library_name
68        self.helper_file_type = helper_file_type
69#
70# HelperFileOutputGenerator - subclass of OutputGenerator. Outputs Vulkan helper files
71class HelperFileOutputGenerator(OutputGenerator):
72    """Generate helper file based on XML element attributes"""
73    def __init__(self,
74                 errFile = sys.stderr,
75                 warnFile = sys.stderr,
76                 diagFile = sys.stdout):
77        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
78        # Internal state - accumulators for different inner block text
79        self.enum_output = ''                             # string built up of enum string routines
80        # Internal state - accumulators for different inner block text
81        self.structNames = []                             # List of Vulkan struct typenames
82        self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
83        self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
84        self.object_types = []                            # List of all handle types
85        self.object_type_aliases = []                     # Aliases to handles types (for handles that were extensions)
86        self.debug_report_object_types = []               # Handy copy of debug_report_object_type enum data
87        self.core_object_types = []                       # Handy copy of core_object_type enum data
88        self.device_extension_info = dict()               # Dict of device extension name defines and ifdef values
89        self.instance_extension_info = dict()             # Dict of instance extension name defines and ifdef values
90
91        # Named tuples to store struct and command data
92        self.StructType = namedtuple('StructType', ['name', 'value'])
93        self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl'])
94        self.StructMemberData = namedtuple('StructMemberData', ['name', 'members', 'ifdef_protect'])
95
96        self.custom_construct_params = {
97            # safe_VkGraphicsPipelineCreateInfo needs to know if subpass has color and\or depth\stencil attachments to use its pointers
98            'VkGraphicsPipelineCreateInfo' :
99                ', const bool uses_color_attachment, const bool uses_depthstencil_attachment',
100            # safe_VkPipelineViewportStateCreateInfo needs to know if viewport and scissor is dynamic to use its pointers
101            'VkPipelineViewportStateCreateInfo' :
102                ', const bool is_dynamic_viewports, const bool is_dynamic_scissors',
103        }
104    #
105    # Called once at the beginning of each run
106    def beginFile(self, genOpts):
107        OutputGenerator.beginFile(self, genOpts)
108        # User-supplied prefix text, if any (list of strings)
109        self.helper_file_type = genOpts.helper_file_type
110        self.library_name = genOpts.library_name
111        # File Comment
112        file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
113        file_comment += '// See helper_file_generator.py for modifications\n'
114        write(file_comment, file=self.outFile)
115        # Copyright Notice
116        copyright = ''
117        copyright += '\n'
118        copyright += '/***************************************************************************\n'
119        copyright += ' *\n'
120        copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
121        copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
122        copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
123        copyright += ' * Copyright (c) 2015-2017 Google Inc.\n'
124        copyright += ' *\n'
125        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
126        copyright += ' * you may not use this file except in compliance with the License.\n'
127        copyright += ' * You may obtain a copy of the License at\n'
128        copyright += ' *\n'
129        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
130        copyright += ' *\n'
131        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
132        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
133        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
134        copyright += ' * See the License for the specific language governing permissions and\n'
135        copyright += ' * limitations under the License.\n'
136        copyright += ' *\n'
137        copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
138        copyright += ' * Author: Courtney Goeltzenleuchter <courtneygo@google.com>\n'
139        copyright += ' * Author: Tobin Ehlis <tobine@google.com>\n'
140        copyright += ' * Author: Chris Forbes <chrisforbes@google.com>\n'
141        copyright += ' * Author: John Zulauf<jzulauf@lunarg.com>\n'
142        copyright += ' *\n'
143        copyright += ' ****************************************************************************/\n'
144        write(copyright, file=self.outFile)
145    #
146    # Write generated file content to output file
147    def endFile(self):
148        dest_file = ''
149        dest_file += self.OutputDestFile()
150        # Remove blank lines at EOF
151        if dest_file.endswith('\n'):
152            dest_file = dest_file[:-1]
153        write(dest_file, file=self.outFile);
154        # Finish processing in superclass
155        OutputGenerator.endFile(self)
156    #
157    # Override parent class to be notified of the beginning of an extension
158    def beginFeature(self, interface, emit):
159        # Start processing in superclass
160        OutputGenerator.beginFeature(self, interface, emit)
161        self.featureExtraProtect = GetFeatureProtect(interface)
162
163        if self.featureName == 'VK_VERSION_1_0' or self.featureName == 'VK_VERSION_1_1':
164            return
165        name = self.featureName
166        nameElem = interface[0][1]
167        name_define = nameElem.get('name')
168        if 'EXTENSION_NAME' not in name_define:
169            print("Error in vk.xml file -- extension name is not available")
170        requires = interface.get('requires')
171        if requires is not None:
172            required_extensions = requires.split(',')
173        else:
174            required_extensions = list()
175        info = { 'define': name_define, 'ifdef':self.featureExtraProtect, 'reqs':required_extensions }
176        if interface.get('type') == 'instance':
177            self.instance_extension_info[name] = info
178        else:
179            self.device_extension_info[name] = info
180
181    #
182    # Override parent class to be notified of the end of an extension
183    def endFeature(self):
184        # Finish processing in superclass
185        OutputGenerator.endFeature(self)
186    #
187    # Grab group (e.g. C "enum" type) info to output for enum-string conversion helper
188    def genGroup(self, groupinfo, groupName, alias):
189        OutputGenerator.genGroup(self, groupinfo, groupName, alias)
190        groupElem = groupinfo.elem
191        # For enum_string_header
192        if self.helper_file_type == 'enum_string_header':
193            value_set = set()
194            for elem in groupElem.findall('enum'):
195                if elem.get('supported') != 'disabled' and elem.get('alias') is None:
196                    value_set.add(elem.get('name'))
197            if value_set != set():
198                self.enum_output += self.GenerateEnumStringConversion(groupName, value_set)
199        elif self.helper_file_type == 'object_types_header':
200            if groupName == 'VkDebugReportObjectTypeEXT':
201                for elem in groupElem.findall('enum'):
202                    if elem.get('supported') != 'disabled':
203                        item_name = elem.get('name')
204                        self.debug_report_object_types.append(item_name)
205            elif groupName == 'VkObjectType':
206                for elem in groupElem.findall('enum'):
207                    if elem.get('supported') != 'disabled':
208                        item_name = elem.get('name')
209                        self.core_object_types.append(item_name)
210
211    #
212    # Called for each type -- if the type is a struct/union, grab the metadata
213    def genType(self, typeinfo, name, alias):
214        OutputGenerator.genType(self, typeinfo, name, alias)
215        typeElem = typeinfo.elem
216        # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
217        # Otherwise, emit the tag text.
218        category = typeElem.get('category')
219        if category == 'handle':
220            if alias:
221                self.object_type_aliases.append((name,alias))
222            else:
223                self.object_types.append(name)
224        elif (category == 'struct' or category == 'union'):
225            self.structNames.append(name)
226            self.genStruct(typeinfo, name, alias)
227    #
228    # Check if the parameter passed in is a pointer
229    def paramIsPointer(self, param):
230        ispointer = False
231        for elem in param:
232            if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
233                ispointer = True
234        return ispointer
235    #
236    # Check if the parameter passed in is a static array
237    def paramIsStaticArray(self, param):
238        isstaticarray = 0
239        paramname = param.find('name')
240        if (paramname.tail is not None) and ('[' in paramname.tail):
241            isstaticarray = paramname.tail.count('[')
242        return isstaticarray
243    #
244    # Retrieve the type and name for a parameter
245    def getTypeNameTuple(self, param):
246        type = ''
247        name = ''
248        for elem in param:
249            if elem.tag == 'type':
250                type = noneStr(elem.text)
251            elif elem.tag == 'name':
252                name = noneStr(elem.text)
253        return (type, name)
254    # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
255    # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
256    def parseLateXMath(self, source):
257        name = 'ERROR'
258        decoratedName = 'ERROR'
259        if 'mathit' in source:
260            # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
261            match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
262            if not match or match.group(1) != match.group(4):
263                raise 'Unrecognized latexmath expression'
264            name = match.group(2)
265            # Need to add 1 for ceiling function; otherwise, the allocated packet
266            # size will be less than needed during capture for some title which use
267            # this in VkPipelineMultisampleStateCreateInfo. based on ceiling function
268            # definition,it is '{0}%{1}?{0}/{1} + 1:{0}/{1}'.format(*match.group(2, 3)),
269            # its value <= '{}/{} + 1'.
270            if match.group(1) == 'ceil':
271                decoratedName = '{}/{} + 1'.format(*match.group(2, 3))
272            else:
273                decoratedName = '{}/{}'.format(*match.group(2, 3))
274        else:
275            # Matches expressions similar to 'latexmath : [dataSize \over 4]'
276            match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
277            name = match.group(2)
278            decoratedName = '{}/{}'.format(*match.group(2, 3))
279        return name, decoratedName
280    #
281    # Retrieve the value of the len tag
282    def getLen(self, param):
283        result = None
284        len = param.attrib.get('len')
285        if len and len != 'null-terminated':
286            # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
287            # have a null terminated array of strings.  We strip the null-terminated from the
288            # 'len' field and only return the parameter specifying the string count
289            if 'null-terminated' in len:
290                result = len.split(',')[0]
291            else:
292                result = len
293            if 'latexmath' in len:
294                param_type, param_name = self.getTypeNameTuple(param)
295                len_name, result = self.parseLateXMath(len)
296            # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
297            result = str(result).replace('::', '->')
298        return result
299    #
300    # Check if a structure is or contains a dispatchable (dispatchable = True) or
301    # non-dispatchable (dispatchable = False) handle
302    def TypeContainsObjectHandle(self, handle_type, dispatchable):
303        if dispatchable:
304            type_key = 'VK_DEFINE_HANDLE'
305        else:
306            type_key = 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
307        handle = self.registry.tree.find("types/type/[name='" + handle_type + "'][@category='handle']")
308        if handle is not None and handle.find('type').text == type_key:
309            return True
310        # if handle_type is a struct, search its members
311        if handle_type in self.structNames:
312            member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == handle_type), None)
313            if member_index is not None:
314                for item in self.structMembers[member_index].members:
315                    handle = self.registry.tree.find("types/type/[name='" + item.type + "'][@category='handle']")
316                    if handle is not None and handle.find('type').text == type_key:
317                        return True
318        return False
319    #
320    # Generate local ready-access data describing Vulkan structures and unions from the XML metadata
321    def genStruct(self, typeinfo, typeName, alias):
322        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
323        members = typeinfo.elem.findall('.//member')
324        # Iterate over members once to get length parameters for arrays
325        lens = set()
326        for member in members:
327            len = self.getLen(member)
328            if len:
329                lens.add(len)
330        # Generate member info
331        membersInfo = []
332        for member in members:
333            # Get the member's type and name
334            info = self.getTypeNameTuple(member)
335            type = info[0]
336            name = info[1]
337            cdecl = self.makeCParamDecl(member, 1)
338            # Process VkStructureType
339            if type == 'VkStructureType':
340                # Extract the required struct type value from the comments
341                # embedded in the original text defining the 'typeinfo' element
342                rawXml = etree.tostring(typeinfo.elem).decode('ascii')
343                result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
344                if result:
345                    value = result.group(0)
346                    # Store the required type value
347                    self.structTypes[typeName] = self.StructType(name=name, value=value)
348            # Store pointer/array/string info
349            isstaticarray = self.paramIsStaticArray(member)
350            membersInfo.append(self.CommandParam(type=type,
351                                                 name=name,
352                                                 ispointer=self.paramIsPointer(member),
353                                                 isstaticarray=isstaticarray,
354                                                 isconst=True if 'const' in cdecl else False,
355                                                 iscount=True if name in lens else False,
356                                                 len=self.getLen(member),
357                                                 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
358                                                 cdecl=cdecl))
359        self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo, ifdef_protect=self.featureExtraProtect))
360    #
361    # Enum_string_header: Create a routine to convert an enumerated value into a string
362    def GenerateEnumStringConversion(self, groupName, value_list):
363        outstring = '\n'
364        outstring += 'static inline const char* string_%s(%s input_value)\n' % (groupName, groupName)
365        outstring += '{\n'
366        outstring += '    switch ((%s)input_value)\n' % groupName
367        outstring += '    {\n'
368        # Emit these in a repeatable order so file is generated with the same contents each time.
369        # This helps compiler caching systems like ccache.
370        for item in sorted(value_list):
371            outstring += '        case %s:\n' % item
372            outstring += '            return "%s";\n' % item
373        outstring += '        default:\n'
374        outstring += '            return "Unhandled %s";\n' % groupName
375        outstring += '    }\n'
376        outstring += '}\n'
377        return outstring
378    #
379    # Tack on a helper which, given an index into a VkPhysicalDeviceFeatures structure, will print the corresponding feature name
380    def DeIndexPhysDevFeatures(self):
381        pdev_members = None
382        for name, members, ifdef in self.structMembers:
383            if name == 'VkPhysicalDeviceFeatures':
384                pdev_members = members
385                break
386        deindex = '\n'
387        deindex += 'static inline const char * GetPhysDevFeatureString(uint32_t index) {\n'
388        deindex += '    const char * IndexToPhysDevFeatureString[] = {\n'
389        for feature in pdev_members:
390            deindex += '        "%s",\n' % feature.name
391        deindex += '    };\n\n'
392        deindex += '    return IndexToPhysDevFeatureString[index];\n'
393        deindex += '}\n'
394        return deindex
395    #
396    # Combine enum string helper header file preamble with body text and return
397    def GenerateEnumStringHelperHeader(self):
398            enum_string_helper_header = '\n'
399            enum_string_helper_header += '#pragma once\n'
400            enum_string_helper_header += '#ifdef _WIN32\n'
401            enum_string_helper_header += '#pragma warning( disable : 4065 )\n'
402            enum_string_helper_header += '#endif\n'
403            enum_string_helper_header += '\n'
404            enum_string_helper_header += '#include <vulkan/vulkan.h>\n'
405            enum_string_helper_header += '\n'
406            enum_string_helper_header += self.enum_output
407            enum_string_helper_header += self.DeIndexPhysDevFeatures()
408            return enum_string_helper_header
409    #
410    # Helper function for declaring a counter variable only once
411    def DeclareCounter(self, string_var, declare_flag):
412        if declare_flag == False:
413            string_var += '        uint32_t i = 0;\n'
414            declare_flag = True
415        return string_var, declare_flag
416    #
417    # Combine safe struct helper header file preamble with body text and return
418    def GenerateSafeStructHelperHeader(self):
419        safe_struct_helper_header = '\n'
420        safe_struct_helper_header += '#pragma once\n'
421        safe_struct_helper_header += '#include <vulkan/vulkan.h>\n'
422        safe_struct_helper_header += '\n'
423        safe_struct_helper_header += self.GenerateSafeStructHeader()
424        return safe_struct_helper_header
425    #
426    # safe_struct header: build function prototypes for header file
427    def GenerateSafeStructHeader(self):
428        safe_struct_header = ''
429        for item in self.structMembers:
430            if self.NeedSafeStruct(item) == True:
431                safe_struct_header += '\n'
432                if item.ifdef_protect is not None:
433                    safe_struct_header += '#ifdef %s\n' % item.ifdef_protect
434                safe_struct_header += 'struct safe_%s {\n' % (item.name)
435                for member in item.members:
436                    if member.type in self.structNames:
437                        member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == member.type), None)
438                        if member_index is not None and self.NeedSafeStruct(self.structMembers[member_index]) == True:
439                            if member.ispointer:
440                                safe_struct_header += '    safe_%s* %s;\n' % (member.type, member.name)
441                            else:
442                                safe_struct_header += '    safe_%s %s;\n' % (member.type, member.name)
443                            continue
444                    if member.len is not None and (self.TypeContainsObjectHandle(member.type, True) or self.TypeContainsObjectHandle(member.type, False)):
445                            safe_struct_header += '    %s* %s;\n' % (member.type, member.name)
446                    else:
447                        safe_struct_header += '%s;\n' % member.cdecl
448                safe_struct_header += '    safe_%s(const %s* in_struct%s);\n' % (item.name, item.name, self.custom_construct_params.get(item.name, ''))
449                safe_struct_header += '    safe_%s(const safe_%s& src);\n' % (item.name, item.name)
450                safe_struct_header += '    safe_%s& operator=(const safe_%s& src);\n' % (item.name, item.name)
451                safe_struct_header += '    safe_%s();\n' % item.name
452                safe_struct_header += '    ~safe_%s();\n' % item.name
453                safe_struct_header += '    void initialize(const %s* in_struct%s);\n' % (item.name, self.custom_construct_params.get(item.name, ''))
454                safe_struct_header += '    void initialize(const safe_%s* src);\n' % (item.name)
455                safe_struct_header += '    %s *ptr() { return reinterpret_cast<%s *>(this); }\n' % (item.name, item.name)
456                safe_struct_header += '    %s const *ptr() const { return reinterpret_cast<%s const *>(this); }\n' % (item.name, item.name)
457                safe_struct_header += '};\n'
458                if item.ifdef_protect is not None:
459                    safe_struct_header += '#endif // %s\n' % item.ifdef_protect
460        return safe_struct_header
461    #
462    # Generate extension helper header file
463    def GenerateExtensionHelperHeader(self):
464
465        V_1_0_instance_extensions_promoted_to_core = [
466            'vk_khr_device_group_creation',
467            'vk_khr_external_fence_capabilities',
468            'vk_khr_external_memory_capabilities',
469            'vk_khr_external_semaphore_capabilities',
470            'vk_khr_get_physical_device_properties_2',
471            ]
472
473        V_1_0_device_extensions_promoted_to_core = [
474            'vk_khr_16bit_storage',
475            'vk_khr_bind_memory_2',
476            'vk_khr_dedicated_allocation',
477            'vk_khr_descriptor_update_template',
478            'vk_khr_device_group',
479            'vk_khr_external_fence',
480            'vk_khr_external_memory',
481            'vk_khr_external_semaphore',
482            'vk_khr_get_memory_requirements_2',
483            'vk_khr_maintenance1',
484            'vk_khr_maintenance2',
485            'vk_khr_maintenance3',
486            'vk_khr_multiview',
487            'vk_khr_relaxed_block_layout',
488            'vk_khr_sampler_ycbcr_conversion',
489            'vk_khr_shader_draw_parameters',
490            'vk_khr_storage_buffer_storage_class',
491            'vk_khr_variable_pointers',
492            ]
493
494        output = [
495            '',
496            '#ifndef VK_EXTENSION_HELPER_H_',
497            '#define VK_EXTENSION_HELPER_H_',
498            '#include <unordered_set>',
499            '#include <string>',
500            '#include <unordered_map>',
501            '#include <utility>',
502            '#include <set>',
503            '',
504            '#include <vulkan/vulkan.h>',
505            '']
506
507        def guarded(ifdef, value):
508            if ifdef is not None:
509                return '\n'.join([ '#ifdef %s' % ifdef, value, '#endif' ])
510            else:
511                return value
512
513        for type in ['Instance', 'Device']:
514            struct_type = '%sExtensions' % type
515            if type == 'Instance':
516                extension_dict = self.instance_extension_info
517                promoted_ext_list = V_1_0_instance_extensions_promoted_to_core
518                struct_decl = 'struct %s {' % struct_type
519                instance_struct_type = struct_type
520            else:
521                extension_dict = self.device_extension_info
522                promoted_ext_list = V_1_0_device_extensions_promoted_to_core
523                struct_decl = 'struct %s : public %s {' % (struct_type, instance_struct_type)
524
525            extension_items = sorted(extension_dict.items())
526
527            field_name = { ext_name: re.sub('_extension_name', '', info['define'].lower()) for ext_name, info in extension_items }
528            if type == 'Instance':
529                instance_field_name = field_name
530                instance_extension_dict = extension_dict
531            else:
532                # Get complete field name and extension data for both Instance and Device extensions
533                field_name.update(instance_field_name)
534                extension_dict = extension_dict.copy()  # Don't modify the self.<dict> we're pointing to
535                extension_dict.update(instance_extension_dict)
536
537            # Output the data member list
538            struct  = [struct_decl]
539            struct.extend([ '    bool %s{false};' % field_name[ext_name] for ext_name, info in extension_items])
540
541            # Create struct entries for saving extension count and extension list from Instance, DeviceCreateInfo
542            if type == 'Instance':
543                struct.extend([
544                    '',
545                    '    std::unordered_set<std::string> device_extension_set;'])
546
547            # Construct the extension information map -- mapping name to data member (field), and required extensions
548            # The map is contained within a static function member for portability reasons.
549            info_type = '%sInfo' % type
550            info_map_type = '%sMap' % info_type
551            req_type = '%sReq' % type
552            req_vec_type = '%sVec' % req_type
553            struct.extend([
554                '',
555                '    struct %s {' % req_type,
556                '        const bool %s::* enabled;' % struct_type,
557                '        const char *name;',
558                '    };',
559                '    typedef std::vector<%s> %s;' % (req_type, req_vec_type),
560                '    struct %s {' % info_type,
561                '       %s(bool %s::* state_, const %s requires_): state(state_), requires(requires_) {}' % ( info_type, struct_type, req_vec_type),
562                '       bool %s::* state;' % struct_type,
563                '       %s requires;' % req_vec_type,
564                '    };',
565                '',
566                '    typedef std::unordered_map<std::string,%s> %s;' % (info_type, info_map_type),
567                '    static const %s &get_info(const char *name) {' %info_type,
568                '        static const %s info_map = {' % info_map_type ])
569
570            field_format = '&' + struct_type + '::%s'
571            req_format = '{' + field_format+ ', %s}'
572            req_indent = '\n                           '
573            req_join = ',' + req_indent
574            info_format = ('            std::make_pair(%s, ' + info_type + '(' + field_format + ', {%s})),')
575            def format_info(ext_name, info):
576                reqs = req_join.join([req_format % (field_name[req], extension_dict[req]['define']) for req in info['reqs']])
577                return info_format % (info['define'], field_name[ext_name], '{%s}' % (req_indent + reqs) if reqs else '')
578
579            struct.extend([guarded(info['ifdef'], format_info(ext_name, info)) for ext_name, info in extension_items])
580            struct.extend([
581                '        };',
582                '',
583                '        static const %s empty_info {nullptr, %s()};' % (info_type, req_vec_type),
584                '        %s::const_iterator info = info_map.find(name);' % info_map_type,
585                '        if ( info != info_map.cend()) {',
586                '            return info->second;',
587                '        }',
588                '        return empty_info;',
589                '    }',
590                ''])
591
592            if type == 'Instance':
593                struct.extend([
594                    '    uint32_t NormalizeApiVersion(uint32_t specified_version) {',
595                    '        uint32_t api_version = (specified_version < VK_API_VERSION_1_1) ? VK_API_VERSION_1_0 : VK_API_VERSION_1_1;',
596                    '        return api_version;',
597                    '    }',
598                    '',
599                    '    uint32_t InitFromInstanceCreateInfo(uint32_t requested_api_version, const VkInstanceCreateInfo *pCreateInfo) {'])
600            else:
601                struct.extend([
602                    '    %s() = default;' % struct_type,
603                    '    %s(const %s& instance_ext) : %s(instance_ext) {}' % (struct_type, instance_struct_type, instance_struct_type),
604                    '',
605                    '    uint32_t InitFromDeviceCreateInfo(const %s *instance_extensions, uint32_t requested_api_version,' % instance_struct_type,
606                    '                                      const VkDeviceCreateInfo *pCreateInfo) {',
607                    '        // Initialize: this to defaults,  base class fields to input.',
608                    '        assert(instance_extensions);',
609                    '        *this = %s(*instance_extensions);' % struct_type,
610                    '']),
611            struct.extend([
612                    '',
613                    '        // Save pCreateInfo device extension list',
614                    '        for (uint32_t extn = 0; extn < pCreateInfo->enabledExtensionCount; extn++) {',
615                    '           device_extension_set.insert(pCreateInfo->ppEnabledExtensionNames[extn]);',
616                    '        }',
617                '',
618                '        static const std::vector<const char *> V_1_0_promoted_%s_extensions = {' % type.lower() ])
619            struct.extend(['            %s_EXTENSION_NAME,' % ext_name.upper() for ext_name in promoted_ext_list])
620            struct.extend([
621                '        };',
622                '',
623                '        // Initialize struct data, robust to invalid pCreateInfo',
624                '        if (pCreateInfo->ppEnabledExtensionNames) {',
625                '            for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {',
626                '                if (!pCreateInfo->ppEnabledExtensionNames[i]) continue;',
627                '                auto info = get_info(pCreateInfo->ppEnabledExtensionNames[i]);',
628                '                if(info.state) this->*(info.state) = true;',
629                '            }',
630                '        }',
631                '        uint32_t api_version = NormalizeApiVersion(requested_api_version);',
632                '        if (api_version >= VK_API_VERSION_1_1) {',
633                '            for (auto promoted_ext : V_1_0_promoted_%s_extensions) {' % type.lower(),
634                '                auto info = get_info(promoted_ext);',
635                '                assert(info.state);',
636                '                if (info.state) this->*(info.state) = true;',
637                '            }',
638                '        }',
639                '        return api_version;',
640                '    }',
641                '};'])
642
643            # Output reference lists of instance/device extension names
644            struct.extend(['', 'static const std::set<std::string> k%sExtensionNames = {' % type])
645            struct.extend([guarded(info['ifdef'], '    %s,' % info['define']) for ext_name, info in extension_items])
646            struct.extend(['};', ''])
647            output.extend(struct)
648
649        output.extend(['', '#endif // VK_EXTENSION_HELPER_H_'])
650        return '\n'.join(output)
651    #
652    # Combine object types helper header file preamble with body text and return
653    def GenerateObjectTypesHelperHeader(self):
654        object_types_helper_header = '\n'
655        object_types_helper_header += '#pragma once\n'
656        object_types_helper_header += '\n'
657        object_types_helper_header += '#include <vulkan/vulkan.h>\n\n'
658        object_types_helper_header += self.GenerateObjectTypesHeader()
659        return object_types_helper_header
660    #
661    # Object types header: create object enum type header file
662    def GenerateObjectTypesHeader(self):
663        object_types_header = ''
664        object_types_header += '// Object Type enum for validation layer internal object handling\n'
665        object_types_header += 'typedef enum VulkanObjectType {\n'
666        object_types_header += '    kVulkanObjectTypeUnknown = 0,\n'
667        enum_num = 1
668        type_list = [];
669        enum_entry_map = {}
670
671        # Output enum definition as each handle is processed, saving the names to use for the conversion routine
672        for item in self.object_types:
673            fixup_name = item[2:]
674            enum_entry = 'kVulkanObjectType%s' % fixup_name
675            enum_entry_map[item] = enum_entry
676            object_types_header += '    ' + enum_entry
677            object_types_header += ' = %d,\n' % enum_num
678            enum_num += 1
679            type_list.append(enum_entry)
680        object_types_header += '    kVulkanObjectTypeMax = %d,\n' % enum_num
681        object_types_header += '    // Aliases for backwards compatibilty of "promoted" types\n'
682        for (name, alias) in self.object_type_aliases:
683            fixup_name = name[2:]
684            object_types_header += '    kVulkanObjectType{} = {},\n'.format(fixup_name, enum_entry_map[alias])
685        object_types_header += '} VulkanObjectType;\n\n'
686
687        # Output name string helper
688        object_types_header += '// Array of object name strings for OBJECT_TYPE enum conversion\n'
689        object_types_header += 'static const char * const object_string[kVulkanObjectTypeMax] = {\n'
690        object_types_header += '    "Unknown",\n'
691        for item in self.object_types:
692            fixup_name = item[2:]
693            object_types_header += '    "%s",\n' % fixup_name
694        object_types_header += '};\n'
695
696        # Key creation helper for map comprehensions that convert between k<Name> and VK<Name> symbols
697        def to_key(regex, raw_key): return re.search(regex, raw_key).group(1).lower().replace("_","")
698
699        # Output a conversion routine from the layer object definitions to the debug report definitions
700        # As the VK_DEBUG_REPORT types are not being updated, specify UNKNOWN for unmatched types
701        object_types_header += '\n'
702        object_types_header += '// Helper array to get Vulkan VK_EXT_debug_report object type enum from the internal layers version\n'
703        object_types_header += 'const VkDebugReportObjectTypeEXT get_debug_report_enum[] = {\n'
704        object_types_header += '    VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, // kVulkanObjectTypeUnknown\n'
705
706        dbg_re = '^VK_DEBUG_REPORT_OBJECT_TYPE_(.*)_EXT$'
707        dbg_map = {to_key(dbg_re, dbg) : dbg for dbg in self.debug_report_object_types}
708        dbg_default = 'VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT'
709        for object_type in type_list:
710            vk_object_type = dbg_map.get(object_type.replace("kVulkanObjectType", "").lower(), dbg_default)
711            object_types_header += '    %s,   // %s\n' % (vk_object_type, object_type)
712        object_types_header += '};\n'
713
714        # Output a conversion routine from the layer object definitions to the core object type definitions
715        # This will intentionally *fail* for unmatched types as the VK_OBJECT_TYPE list should match the kVulkanObjectType list
716        object_types_header += '\n'
717        object_types_header += '// Helper array to get Official Vulkan VkObjectType enum from the internal layers version\n'
718        object_types_header += 'const VkObjectType get_object_type_enum[] = {\n'
719        object_types_header += '    VK_OBJECT_TYPE_UNKNOWN, // kVulkanObjectTypeUnknown\n'
720
721        vko_re = '^VK_OBJECT_TYPE_(.*)'
722        vko_map = {to_key(vko_re, vko) : vko for vko in self.core_object_types}
723        for object_type in type_list:
724            vk_object_type = vko_map[object_type.replace("kVulkanObjectType", "").lower()]
725            object_types_header += '    %s,   // %s\n' % (vk_object_type, object_type)
726        object_types_header += '};\n'
727
728        # Create a function to convert from VkDebugReportObjectTypeEXT to VkObjectType
729        object_types_header += '\n'
730        object_types_header += '// Helper function to convert from VkDebugReportObjectTypeEXT to VkObjectType\n'
731        object_types_header += 'static inline VkObjectType convertDebugReportObjectToCoreObject(VkDebugReportObjectTypeEXT debug_report_obj){\n'
732        object_types_header += '    if (debug_report_obj == VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT) {\n'
733        object_types_header += '        return VK_OBJECT_TYPE_UNKNOWN;\n'
734        for core_object_type in self.core_object_types:
735            core_target_type = core_object_type.replace("VK_OBJECT_TYPE_", "").lower()
736            core_target_type = core_target_type.replace("_", "")
737            for dr_object_type in self.debug_report_object_types:
738                dr_target_type = dr_object_type.replace("VK_DEBUG_REPORT_OBJECT_TYPE_", "").lower()
739                dr_target_type = dr_target_type[:-4]
740                dr_target_type = dr_target_type.replace("_", "")
741                if core_target_type == dr_target_type:
742                    object_types_header += '    } else if (debug_report_obj == %s) {\n' % dr_object_type
743                    object_types_header += '        return %s;\n' % core_object_type
744                    break
745        object_types_header += '    }\n'
746        object_types_header += '    return VK_OBJECT_TYPE_UNKNOWN;\n'
747        object_types_header += '}\n'
748
749        # Create a function to convert from VkObjectType to VkDebugReportObjectTypeEXT
750        object_types_header += '\n'
751        object_types_header += '// Helper function to convert from VkDebugReportObjectTypeEXT to VkObjectType\n'
752        object_types_header += 'static inline VkDebugReportObjectTypeEXT convertCoreObjectToDebugReportObject(VkObjectType core_report_obj){\n'
753        object_types_header += '    if (core_report_obj == VK_OBJECT_TYPE_UNKNOWN) {\n'
754        object_types_header += '        return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;\n'
755        for core_object_type in self.core_object_types:
756            core_target_type = core_object_type.replace("VK_OBJECT_TYPE_", "").lower()
757            core_target_type = core_target_type.replace("_", "")
758            for dr_object_type in self.debug_report_object_types:
759                dr_target_type = dr_object_type.replace("VK_DEBUG_REPORT_OBJECT_TYPE_", "").lower()
760                dr_target_type = dr_target_type[:-4]
761                dr_target_type = dr_target_type.replace("_", "")
762                if core_target_type == dr_target_type:
763                    object_types_header += '    } else if (core_report_obj == %s) {\n' % core_object_type
764                    object_types_header += '        return %s;\n' % dr_object_type
765                    break
766        object_types_header += '    }\n'
767        object_types_header += '    return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;\n'
768        object_types_header += '}\n'
769        return object_types_header
770    #
771    # Determine if a structure needs a safe_struct helper function
772    # That is, it has an sType or one of its members is a pointer
773    def NeedSafeStruct(self, structure):
774        if 'sType' == structure.name:
775            return True
776        for member in structure.members:
777            if member.ispointer == True:
778                return True
779        return False
780    #
781    # Combine safe struct helper source file preamble with body text and return
782    def GenerateSafeStructHelperSource(self):
783        safe_struct_helper_source = '\n'
784        safe_struct_helper_source += '#include "vk_safe_struct.h"\n'
785        safe_struct_helper_source += '#include <string.h>\n'
786        safe_struct_helper_source += '\n'
787        safe_struct_helper_source += self.GenerateSafeStructSource()
788        return safe_struct_helper_source
789    #
790    # safe_struct source -- create bodies of safe struct helper functions
791    def GenerateSafeStructSource(self):
792        safe_struct_body = []
793        wsi_structs = ['VkXlibSurfaceCreateInfoKHR',
794                       'VkXcbSurfaceCreateInfoKHR',
795                       'VkWaylandSurfaceCreateInfoKHR',
796                       'VkAndroidSurfaceCreateInfoKHR',
797                       'VkWin32SurfaceCreateInfoKHR'
798                       ]
799
800        # For abstract types just want to save the pointer away
801        # since we cannot make a copy.
802        abstract_types = ['AHardwareBuffer',
803                          'ANativeWindow',
804                         ]
805        for item in self.structMembers:
806            if self.NeedSafeStruct(item) == False:
807                continue
808            if item.name in wsi_structs:
809                continue
810            if item.ifdef_protect is not None:
811                safe_struct_body.append("#ifdef %s\n" % item.ifdef_protect)
812            ss_name = "safe_%s" % item.name
813            init_list = ''          # list of members in struct constructor initializer
814            default_init_list = ''  # Default constructor just inits ptrs to nullptr in initializer
815            init_func_txt = ''      # Txt for initialize() function that takes struct ptr and inits members
816            construct_txt = ''      # Body of constuctor as well as body of initialize() func following init_func_txt
817            destruct_txt = ''
818
819            custom_construct_txt = {
820                # VkWriteDescriptorSet is special case because pointers may be non-null but ignored
821                'VkWriteDescriptorSet' :
822                    '    switch (descriptorType) {\n'
823                    '        case VK_DESCRIPTOR_TYPE_SAMPLER:\n'
824                    '        case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:\n'
825                    '        case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n'
826                    '        case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:\n'
827                    '        case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:\n'
828                    '        if (descriptorCount && in_struct->pImageInfo) {\n'
829                    '            pImageInfo = new VkDescriptorImageInfo[descriptorCount];\n'
830                    '            for (uint32_t i=0; i<descriptorCount; ++i) {\n'
831                    '                pImageInfo[i] = in_struct->pImageInfo[i];\n'
832                    '            }\n'
833                    '        }\n'
834                    '        break;\n'
835                    '        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n'
836                    '        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n'
837                    '        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:\n'
838                    '        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:\n'
839                    '        if (descriptorCount && in_struct->pBufferInfo) {\n'
840                    '            pBufferInfo = new VkDescriptorBufferInfo[descriptorCount];\n'
841                    '            for (uint32_t i=0; i<descriptorCount; ++i) {\n'
842                    '                pBufferInfo[i] = in_struct->pBufferInfo[i];\n'
843                    '            }\n'
844                    '        }\n'
845                    '        break;\n'
846                    '        case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n'
847                    '        case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:\n'
848                    '        if (descriptorCount && in_struct->pTexelBufferView) {\n'
849                    '            pTexelBufferView = new VkBufferView[descriptorCount];\n'
850                    '            for (uint32_t i=0; i<descriptorCount; ++i) {\n'
851                    '                pTexelBufferView[i] = in_struct->pTexelBufferView[i];\n'
852                    '            }\n'
853                    '        }\n'
854                    '        break;\n'
855                    '        default:\n'
856                    '        break;\n'
857                    '    }\n',
858                'VkShaderModuleCreateInfo' :
859                    '    if (in_struct->pCode) {\n'
860                    '        pCode = reinterpret_cast<uint32_t *>(new uint8_t[codeSize]);\n'
861                    '        memcpy((void *)pCode, (void *)in_struct->pCode, codeSize);\n'
862                    '    }\n',
863                # VkGraphicsPipelineCreateInfo is special case because its pointers may be non-null but ignored
864                'VkGraphicsPipelineCreateInfo' :
865                    '    if (stageCount && in_struct->pStages) {\n'
866                    '        pStages = new safe_VkPipelineShaderStageCreateInfo[stageCount];\n'
867                    '        for (uint32_t i=0; i<stageCount; ++i) {\n'
868                    '            pStages[i].initialize(&in_struct->pStages[i]);\n'
869                    '        }\n'
870                    '    }\n'
871                    '    if (in_struct->pVertexInputState)\n'
872                    '        pVertexInputState = new safe_VkPipelineVertexInputStateCreateInfo(in_struct->pVertexInputState);\n'
873                    '    else\n'
874                    '        pVertexInputState = NULL;\n'
875                    '    if (in_struct->pInputAssemblyState)\n'
876                    '        pInputAssemblyState = new safe_VkPipelineInputAssemblyStateCreateInfo(in_struct->pInputAssemblyState);\n'
877                    '    else\n'
878                    '        pInputAssemblyState = NULL;\n'
879                    '    bool has_tessellation_stage = false;\n'
880                    '    if (stageCount && pStages)\n'
881                    '        for (uint32_t i=0; i<stageCount && !has_tessellation_stage; ++i)\n'
882                    '            if (pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n'
883                    '                has_tessellation_stage = true;\n'
884                    '    if (in_struct->pTessellationState && has_tessellation_stage)\n'
885                    '        pTessellationState = new safe_VkPipelineTessellationStateCreateInfo(in_struct->pTessellationState);\n'
886                    '    else\n'
887                    '        pTessellationState = NULL; // original pTessellationState pointer ignored\n'
888                    '    bool has_rasterization = in_struct->pRasterizationState ? !in_struct->pRasterizationState->rasterizerDiscardEnable : false;\n'
889                    '    if (in_struct->pViewportState && has_rasterization) {\n'
890                    '        bool is_dynamic_viewports = false;\n'
891                    '        bool is_dynamic_scissors = false;\n'
892                    '        if (in_struct->pDynamicState && in_struct->pDynamicState->pDynamicStates) {\n'
893                    '            for (uint32_t i = 0; i < in_struct->pDynamicState->dynamicStateCount && !is_dynamic_viewports; ++i)\n'
894                    '                if (in_struct->pDynamicState->pDynamicStates[i] == VK_DYNAMIC_STATE_VIEWPORT)\n'
895                    '                    is_dynamic_viewports = true;\n'
896                    '            for (uint32_t i = 0; i < in_struct->pDynamicState->dynamicStateCount && !is_dynamic_scissors; ++i)\n'
897                    '                if (in_struct->pDynamicState->pDynamicStates[i] == VK_DYNAMIC_STATE_SCISSOR)\n'
898                    '                    is_dynamic_scissors = true;\n'
899                    '        }\n'
900                    '        pViewportState = new safe_VkPipelineViewportStateCreateInfo(in_struct->pViewportState, is_dynamic_viewports, is_dynamic_scissors);\n'
901                    '    } else\n'
902                    '        pViewportState = NULL; // original pViewportState pointer ignored\n'
903                    '    if (in_struct->pRasterizationState)\n'
904                    '        pRasterizationState = new safe_VkPipelineRasterizationStateCreateInfo(in_struct->pRasterizationState);\n'
905                    '    else\n'
906                    '        pRasterizationState = NULL;\n'
907                    '    if (in_struct->pMultisampleState && has_rasterization)\n'
908                    '        pMultisampleState = new safe_VkPipelineMultisampleStateCreateInfo(in_struct->pMultisampleState);\n'
909                    '    else\n'
910                    '        pMultisampleState = NULL; // original pMultisampleState pointer ignored\n'
911                    '    // needs a tracked subpass state uses_depthstencil_attachment\n'
912                    '    if (in_struct->pDepthStencilState && has_rasterization && uses_depthstencil_attachment)\n'
913                    '        pDepthStencilState = new safe_VkPipelineDepthStencilStateCreateInfo(in_struct->pDepthStencilState);\n'
914                    '    else\n'
915                    '        pDepthStencilState = NULL; // original pDepthStencilState pointer ignored\n'
916                    '    // needs a tracked subpass state usesColorAttachment\n'
917                    '    if (in_struct->pColorBlendState && has_rasterization && uses_color_attachment)\n'
918                    '        pColorBlendState = new safe_VkPipelineColorBlendStateCreateInfo(in_struct->pColorBlendState);\n'
919                    '    else\n'
920                    '        pColorBlendState = NULL; // original pColorBlendState pointer ignored\n'
921                    '    if (in_struct->pDynamicState)\n'
922                    '        pDynamicState = new safe_VkPipelineDynamicStateCreateInfo(in_struct->pDynamicState);\n'
923                    '    else\n'
924                    '        pDynamicState = NULL;\n',
925                 # VkPipelineViewportStateCreateInfo is special case because its pointers may be non-null but ignored
926                'VkPipelineViewportStateCreateInfo' :
927                    '    if (in_struct->pViewports && !is_dynamic_viewports) {\n'
928                    '        pViewports = new VkViewport[in_struct->viewportCount];\n'
929                    '        memcpy ((void *)pViewports, (void *)in_struct->pViewports, sizeof(VkViewport)*in_struct->viewportCount);\n'
930                    '    }\n'
931                    '    else\n'
932                    '        pViewports = NULL;\n'
933                    '    if (in_struct->pScissors && !is_dynamic_scissors) {\n'
934                    '        pScissors = new VkRect2D[in_struct->scissorCount];\n'
935                    '        memcpy ((void *)pScissors, (void *)in_struct->pScissors, sizeof(VkRect2D)*in_struct->scissorCount);\n'
936                    '    }\n'
937                    '    else\n'
938                    '        pScissors = NULL;\n',
939                # VkDescriptorSetLayoutBinding is special case because its pImmutableSamplers pointer may be non-null but ignored
940                'VkDescriptorSetLayoutBinding' :
941                    '    const bool sampler_type = in_struct->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || in_struct->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n'
942                    '    if (descriptorCount && in_struct->pImmutableSamplers && sampler_type) {\n'
943                    '        pImmutableSamplers = new VkSampler[descriptorCount];\n'
944                    '        for (uint32_t i=0; i<descriptorCount; ++i) {\n'
945                    '            pImmutableSamplers[i] = in_struct->pImmutableSamplers[i];\n'
946                    '        }\n'
947                    '    }\n',
948            }
949
950            custom_copy_txt = {
951                # VkGraphicsPipelineCreateInfo is special case because it has custom construct parameters
952                'VkGraphicsPipelineCreateInfo' :
953                    '    if (stageCount && src.pStages) {\n'
954                    '        pStages = new safe_VkPipelineShaderStageCreateInfo[stageCount];\n'
955                    '        for (uint32_t i=0; i<stageCount; ++i) {\n'
956                    '            pStages[i].initialize(&src.pStages[i]);\n'
957                    '        }\n'
958                    '    }\n'
959                    '    if (src.pVertexInputState)\n'
960                    '        pVertexInputState = new safe_VkPipelineVertexInputStateCreateInfo(*src.pVertexInputState);\n'
961                    '    else\n'
962                    '        pVertexInputState = NULL;\n'
963                    '    if (src.pInputAssemblyState)\n'
964                    '        pInputAssemblyState = new safe_VkPipelineInputAssemblyStateCreateInfo(*src.pInputAssemblyState);\n'
965                    '    else\n'
966                    '        pInputAssemblyState = NULL;\n'
967                    '    bool has_tessellation_stage = false;\n'
968                    '    if (stageCount && pStages)\n'
969                    '        for (uint32_t i=0; i<stageCount && !has_tessellation_stage; ++i)\n'
970                    '            if (pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT || pStages[i].stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n'
971                    '                has_tessellation_stage = true;\n'
972                    '    if (src.pTessellationState && has_tessellation_stage)\n'
973                    '        pTessellationState = new safe_VkPipelineTessellationStateCreateInfo(*src.pTessellationState);\n'
974                    '    else\n'
975                    '        pTessellationState = NULL; // original pTessellationState pointer ignored\n'
976                    '    bool has_rasterization = src.pRasterizationState ? !src.pRasterizationState->rasterizerDiscardEnable : false;\n'
977                    '    if (src.pViewportState && has_rasterization) {\n'
978                    '        pViewportState = new safe_VkPipelineViewportStateCreateInfo(*src.pViewportState);\n'
979                    '    } else\n'
980                    '        pViewportState = NULL; // original pViewportState pointer ignored\n'
981                    '    if (src.pRasterizationState)\n'
982                    '        pRasterizationState = new safe_VkPipelineRasterizationStateCreateInfo(*src.pRasterizationState);\n'
983                    '    else\n'
984                    '        pRasterizationState = NULL;\n'
985                    '    if (src.pMultisampleState && has_rasterization)\n'
986                    '        pMultisampleState = new safe_VkPipelineMultisampleStateCreateInfo(*src.pMultisampleState);\n'
987                    '    else\n'
988                    '        pMultisampleState = NULL; // original pMultisampleState pointer ignored\n'
989                    '    if (src.pDepthStencilState && has_rasterization)\n'
990                    '        pDepthStencilState = new safe_VkPipelineDepthStencilStateCreateInfo(*src.pDepthStencilState);\n'
991                    '    else\n'
992                    '        pDepthStencilState = NULL; // original pDepthStencilState pointer ignored\n'
993                    '    if (src.pColorBlendState && has_rasterization)\n'
994                    '        pColorBlendState = new safe_VkPipelineColorBlendStateCreateInfo(*src.pColorBlendState);\n'
995                    '    else\n'
996                    '        pColorBlendState = NULL; // original pColorBlendState pointer ignored\n'
997                    '    if (src.pDynamicState)\n'
998                    '        pDynamicState = new safe_VkPipelineDynamicStateCreateInfo(*src.pDynamicState);\n'
999                    '    else\n'
1000                    '        pDynamicState = NULL;\n',
1001                 # VkPipelineViewportStateCreateInfo is special case because it has custom construct parameters
1002                'VkPipelineViewportStateCreateInfo' :
1003                    '    if (src.pViewports) {\n'
1004                    '        pViewports = new VkViewport[src.viewportCount];\n'
1005                    '        memcpy ((void *)pViewports, (void *)src.pViewports, sizeof(VkViewport)*src.viewportCount);\n'
1006                    '    }\n'
1007                    '    else\n'
1008                    '        pViewports = NULL;\n'
1009                    '    if (src.pScissors) {\n'
1010                    '        pScissors = new VkRect2D[src.scissorCount];\n'
1011                    '        memcpy ((void *)pScissors, (void *)src.pScissors, sizeof(VkRect2D)*src.scissorCount);\n'
1012                    '    }\n'
1013                    '    else\n'
1014                    '        pScissors = NULL;\n',
1015            }
1016
1017            custom_destruct_txt = {'VkShaderModuleCreateInfo' :
1018                                   '    if (pCode)\n'
1019                                   '        delete[] reinterpret_cast<const uint8_t *>(pCode);\n' }
1020
1021            for member in item.members:
1022                m_type = member.type
1023                if member.type in self.structNames:
1024                    member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == member.type), None)
1025                    if member_index is not None and self.NeedSafeStruct(self.structMembers[member_index]) == True:
1026                        m_type = 'safe_%s' % member.type
1027                if member.ispointer and 'safe_' not in m_type and self.TypeContainsObjectHandle(member.type, False) == False:
1028                    # Ptr types w/o a safe_struct, for non-null case need to allocate new ptr and copy data in
1029                    if m_type in ['void', 'char']:
1030                        # For these exceptions just copy initial value over for now
1031                        init_list += '\n    %s(in_struct->%s),' % (member.name, member.name)
1032                        init_func_txt += '    %s = in_struct->%s;\n' % (member.name, member.name)
1033                    else:
1034                        default_init_list += '\n    %s(nullptr),' % (member.name)
1035                        init_list += '\n    %s(nullptr),' % (member.name)
1036                        if m_type in abstract_types:
1037                            construct_txt += '    %s = in_struct->%s;\n' % (member.name, member.name)
1038                        else:
1039                            init_func_txt += '    %s = nullptr;\n' % (member.name)
1040                            if 'pNext' != member.name and 'void' not in m_type:
1041                                if not member.isstaticarray and (member.len is None or '/' in member.len):
1042                                    construct_txt += '    if (in_struct->%s) {\n' % member.name
1043                                    construct_txt += '        %s = new %s(*in_struct->%s);\n' % (member.name, m_type, member.name)
1044                                    construct_txt += '    }\n'
1045                                    destruct_txt += '    if (%s)\n' % member.name
1046                                    destruct_txt += '        delete %s;\n' % member.name
1047                                else:
1048                                    construct_txt += '    if (in_struct->%s) {\n' % member.name
1049                                    construct_txt += '        %s = new %s[in_struct->%s];\n' % (member.name, m_type, member.len)
1050                                    construct_txt += '        memcpy ((void *)%s, (void *)in_struct->%s, sizeof(%s)*in_struct->%s);\n' % (member.name, member.name, m_type, member.len)
1051                                    construct_txt += '    }\n'
1052                                    destruct_txt += '    if (%s)\n' % member.name
1053                                    destruct_txt += '        delete[] %s;\n' % member.name
1054                elif member.isstaticarray or member.len is not None:
1055                    if member.len is None:
1056                        # Extract length of static array by grabbing val between []
1057                        static_array_size = re.match(r"[^[]*\[([^]]*)\]", member.cdecl)
1058                        construct_txt += '    for (uint32_t i=0; i<%s; ++i) {\n' % static_array_size.group(1)
1059                        construct_txt += '        %s[i] = in_struct->%s[i];\n' % (member.name, member.name)
1060                        construct_txt += '    }\n'
1061                    else:
1062                        # Init array ptr to NULL
1063                        default_init_list += '\n    %s(nullptr),' % member.name
1064                        init_list += '\n    %s(nullptr),' % member.name
1065                        init_func_txt += '    %s = nullptr;\n' % member.name
1066                        array_element = 'in_struct->%s[i]' % member.name
1067                        if member.type in self.structNames:
1068                            member_index = next((i for i, v in enumerate(self.structMembers) if v[0] == member.type), None)
1069                            if member_index is not None and self.NeedSafeStruct(self.structMembers[member_index]) == True:
1070                                array_element = '%s(&in_struct->safe_%s[i])' % (member.type, member.name)
1071                        construct_txt += '    if (%s && in_struct->%s) {\n' % (member.len, member.name)
1072                        construct_txt += '        %s = new %s[%s];\n' % (member.name, m_type, member.len)
1073                        destruct_txt += '    if (%s)\n' % member.name
1074                        destruct_txt += '        delete[] %s;\n' % member.name
1075                        construct_txt += '        for (uint32_t i=0; i<%s; ++i) {\n' % (member.len)
1076                        if 'safe_' in m_type:
1077                            construct_txt += '            %s[i].initialize(&in_struct->%s[i]);\n' % (member.name, member.name)
1078                        else:
1079                            construct_txt += '            %s[i] = %s;\n' % (member.name, array_element)
1080                        construct_txt += '        }\n'
1081                        construct_txt += '    }\n'
1082                elif member.ispointer == True:
1083                    construct_txt += '    if (in_struct->%s)\n' % member.name
1084                    construct_txt += '        %s = new %s(in_struct->%s);\n' % (member.name, m_type, member.name)
1085                    construct_txt += '    else\n'
1086                    construct_txt += '        %s = NULL;\n' % member.name
1087                    destruct_txt += '    if (%s)\n' % member.name
1088                    destruct_txt += '        delete %s;\n' % member.name
1089                elif 'safe_' in m_type:
1090                    init_list += '\n    %s(&in_struct->%s),' % (member.name, member.name)
1091                    init_func_txt += '    %s.initialize(&in_struct->%s);\n' % (member.name, member.name)
1092                else:
1093                    init_list += '\n    %s(in_struct->%s),' % (member.name, member.name)
1094                    init_func_txt += '    %s = in_struct->%s;\n' % (member.name, member.name)
1095            if '' != init_list:
1096                init_list = init_list[:-1] # hack off final comma
1097            if item.name in custom_construct_txt:
1098                construct_txt = custom_construct_txt[item.name]
1099            if item.name in custom_destruct_txt:
1100                destruct_txt = custom_destruct_txt[item.name]
1101            safe_struct_body.append("\n%s::%s(const %s* in_struct%s) :%s\n{\n%s}" % (ss_name, ss_name, item.name, self.custom_construct_params.get(item.name, ''), init_list, construct_txt))
1102            if '' != default_init_list:
1103                default_init_list = " :%s" % (default_init_list[:-1])
1104            safe_struct_body.append("\n%s::%s()%s\n{}" % (ss_name, ss_name, default_init_list))
1105            # Create slight variation of init and construct txt for copy constructor that takes a src object reference vs. struct ptr
1106            copy_construct_init = init_func_txt.replace('in_struct->', 'src.')
1107            copy_construct_txt = construct_txt.replace(' (in_struct->', ' (src.')     # Exclude 'if' blocks from next line
1108            copy_construct_txt = copy_construct_txt.replace('(in_struct->', '(*src.') # Pass object to copy constructors
1109            copy_construct_txt = copy_construct_txt.replace('in_struct->', 'src.')    # Modify remaining struct refs for src object
1110            if item.name in custom_copy_txt:
1111                copy_construct_txt = custom_copy_txt[item.name]
1112            copy_assign_txt = '    if (&src == this) return *this;\n\n' + destruct_txt + '\n' + copy_construct_init + copy_construct_txt + '\n    return *this;'
1113            safe_struct_body.append("\n%s::%s(const %s& src)\n{\n%s%s}" % (ss_name, ss_name, ss_name, copy_construct_init, copy_construct_txt)) # Copy constructor
1114            safe_struct_body.append("\n%s& %s::operator=(const %s& src)\n{\n%s\n}" % (ss_name, ss_name, ss_name, copy_assign_txt)) # Copy assignment operator
1115            safe_struct_body.append("\n%s::~%s()\n{\n%s}" % (ss_name, ss_name, destruct_txt))
1116            safe_struct_body.append("\nvoid %s::initialize(const %s* in_struct%s)\n{\n%s%s}" % (ss_name, item.name, self.custom_construct_params.get(item.name, ''), init_func_txt, construct_txt))
1117            # Copy initializer uses same txt as copy constructor but has a ptr and not a reference
1118            init_copy = copy_construct_init.replace('src.', 'src->')
1119            init_construct = copy_construct_txt.replace('src.', 'src->')
1120            safe_struct_body.append("\nvoid %s::initialize(const %s* src)\n{\n%s%s}" % (ss_name, ss_name, init_copy, init_construct))
1121            if item.ifdef_protect is not None:
1122                safe_struct_body.append("#endif // %s\n" % item.ifdef_protect)
1123        return "\n".join(safe_struct_body)
1124    #
1125    # Generate the type map
1126    def GenerateTypeMapHelperHeader(self):
1127        prefix = 'Lvl'
1128        fprefix = 'lvl_'
1129        typemap = prefix + 'TypeMap'
1130        idmap = prefix + 'STypeMap'
1131        type_member = 'Type'
1132        id_member = 'kSType'
1133        id_decl = 'static const VkStructureType '
1134        generic_header = prefix + 'GenericHeader'
1135        typename_func = fprefix + 'typename'
1136        idname_func = fprefix + 'stype_name'
1137        find_func = fprefix + 'find_in_chain'
1138        init_func = fprefix + 'init_struct'
1139
1140        explanatory_comment = '\n'.join((
1141                '// These empty generic templates are specialized for each type with sType',
1142                '// members and for each sType -- providing a two way map between structure',
1143                '// types and sTypes'))
1144
1145        empty_typemap = 'template <typename T> struct ' + typemap + ' {};'
1146        typemap_format  = 'template <> struct {template}<{typename}> {{\n'
1147        typemap_format += '    {id_decl}{id_member} = {id_value};\n'
1148        typemap_format += '}};\n'
1149
1150        empty_idmap = 'template <VkStructureType id> struct ' + idmap + ' {};'
1151        idmap_format = ''.join((
1152            'template <> struct {template}<{id_value}> {{\n',
1153            '    typedef {typename} {typedef};\n',
1154            '}};\n'))
1155
1156        # Define the utilities (here so any renaming stays consistent), if this grows large, refactor to a fixed .h file
1157        utilities_format = '\n'.join((
1158            '// Header "base class" for pNext chain traversal',
1159            'struct {header} {{',
1160            '   VkStructureType sType;',
1161            '   const {header} *pNext;',
1162            '}};',
1163            '',
1164            '// Find an entry of the given type in the pNext chain',
1165            'template <typename T> const T *{find_func}(const void *next) {{',
1166            '    const {header} *current = reinterpret_cast<const {header} *>(next);',
1167            '    const T *found = nullptr;',
1168            '    while (current) {{',
1169            '        if ({type_map}<T>::{id_member} == current->sType) {{',
1170            '            found = reinterpret_cast<const T*>(current);',
1171            '            current = nullptr;',
1172            '        }} else {{',
1173            '            current = current->pNext;',
1174            '        }}',
1175            '    }}',
1176            '    return found;',
1177            '}}',
1178            '',
1179            '// Init the header of an sType struct with pNext',
1180            'template <typename T> T {init_func}(void *p_next) {{',
1181            '    T out = {{}};',
1182            '    out.sType = {type_map}<T>::kSType;',
1183            '    out.pNext = p_next;',
1184            '    return out;',
1185            '}}',
1186                        '',
1187            '// Init the header of an sType struct',
1188            'template <typename T> T {init_func}() {{',
1189            '    T out = {{}};',
1190            '    out.sType = {type_map}<T>::kSType;',
1191            '    return out;',
1192            '}}',
1193
1194            ''))
1195
1196        code = []
1197
1198        # Generate header
1199        code.append('\n'.join((
1200            '#pragma once',
1201            '#include <vulkan/vulkan.h>\n',
1202            explanatory_comment, '',
1203            empty_idmap,
1204            empty_typemap, '')))
1205
1206        # Generate the specializations for each type and stype
1207        for item in self.structMembers:
1208            typename = item.name
1209            info = self.structTypes.get(typename)
1210            if not info:
1211                continue
1212
1213            if item.ifdef_protect is not None:
1214                code.append('#ifdef %s' % item.ifdef_protect)
1215
1216            code.append('// Map type {} to id {}'.format(typename, info.value))
1217            code.append(typemap_format.format(template=typemap, typename=typename, id_value=info.value,
1218                id_decl=id_decl, id_member=id_member))
1219            code.append(idmap_format.format(template=idmap, typename=typename, id_value=info.value, typedef=type_member))
1220
1221            if item.ifdef_protect is not None:
1222                code.append('#endif // %s' % item.ifdef_protect)
1223
1224        # Generate utilities for all types
1225        code.append('\n'.join((
1226            utilities_format.format(id_member=id_member, id_map=idmap, type_map=typemap,
1227                type_member=type_member, header=generic_header, typename_func=typename_func, idname_func=idname_func,
1228                find_func=find_func, init_func=init_func), ''
1229            )))
1230
1231        return "\n".join(code)
1232
1233    #
1234    # Create a helper file and return it as a string
1235    def OutputDestFile(self):
1236        if self.helper_file_type == 'enum_string_header':
1237            return self.GenerateEnumStringHelperHeader()
1238        elif self.helper_file_type == 'safe_struct_header':
1239            return self.GenerateSafeStructHelperHeader()
1240        elif self.helper_file_type == 'safe_struct_source':
1241            return self.GenerateSafeStructHelperSource()
1242        elif self.helper_file_type == 'object_types_header':
1243            return self.GenerateObjectTypesHelperHeader()
1244        elif self.helper_file_type == 'extension_helper_header':
1245            return self.GenerateExtensionHelperHeader()
1246        elif self.helper_file_type == 'typemap_helper_header':
1247            return self.GenerateTypeMapHelperHeader()
1248        else:
1249            return 'Bad Helper File Generator Option %s' % self.helper_file_type
1250