Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/python3 -i
      2 #
      3 # Copyright (c) 2015-2016 The Khronos Group Inc.
      4 # Copyright (c) 2015-2016 Valve Corporation
      5 # Copyright (c) 2015-2016 LunarG, Inc.
      6 # Copyright (c) 2015-2016 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: Tobin Ehlis <tobine (at] google.com>
     21 # Author: Mark Lobodzinski <mark (at] lunarg.com>
     22 
     23 import os,re,sys
     24 import xml.etree.ElementTree as etree
     25 from generator import *
     26 from collections import namedtuple
     27 
     28 # UniqueObjectsGeneratorOptions - subclass of GeneratorOptions.
     29 #
     30 # Adds options used by UniqueObjectsOutputGenerator objects during
     31 # unique objects layer generation.
     32 #
     33 # Additional members
     34 #   prefixText - list of strings to prefix generated header with
     35 #     (usually a copyright statement + calling convention macros).
     36 #   protectFile - True if multiple inclusion protection should be
     37 #     generated (based on the filename) around the entire header.
     38 #   protectFeature - True if #ifndef..#endif protection should be
     39 #     generated around a feature interface in the header file.
     40 #   genFuncPointers - True if function pointer typedefs should be
     41 #     generated
     42 #   protectProto - If conditional protection should be generated
     43 #     around prototype declarations, set to either '#ifdef'
     44 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
     45 #     to require opt-out (#ifndef protectProtoStr). Otherwise
     46 #     set to None.
     47 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
     48 #     declarations, if protectProto is set
     49 #   apicall - string to use for the function declaration prefix,
     50 #     such as APICALL on Windows.
     51 #   apientry - string to use for the calling convention macro,
     52 #     in typedefs, such as APIENTRY.
     53 #   apientryp - string to use for the calling convention macro
     54 #     in function pointer typedefs, such as APIENTRYP.
     55 #   indentFuncProto - True if prototype declarations should put each
     56 #     parameter on a separate line
     57 #   indentFuncPointer - True if typedefed function pointers should put each
     58 #     parameter on a separate line
     59 #   alignFuncParam - if nonzero and parameters are being put on a
     60 #     separate line, align parameter names at the specified column
     61 class UniqueObjectsGeneratorOptions(GeneratorOptions):
     62     def __init__(self,
     63                  filename = None,
     64                  directory = '.',
     65                  apiname = None,
     66                  profile = None,
     67                  versions = '.*',
     68                  emitversions = '.*',
     69                  defaultExtensions = None,
     70                  addExtensions = None,
     71                  removeExtensions = None,
     72                  sortProcedure = regSortFeatures,
     73                  prefixText = "",
     74                  genFuncPointers = True,
     75                  protectFile = True,
     76                  protectFeature = True,
     77                  protectProto = None,
     78                  protectProtoStr = None,
     79                  apicall = '',
     80                  apientry = '',
     81                  apientryp = '',
     82                  indentFuncProto = True,
     83                  indentFuncPointer = False,
     84                  alignFuncParam = 0):
     85         GeneratorOptions.__init__(self, filename, directory, apiname, profile,
     86                                   versions, emitversions, defaultExtensions,
     87                                   addExtensions, removeExtensions, sortProcedure)
     88         self.prefixText      = prefixText
     89         self.genFuncPointers = genFuncPointers
     90         self.protectFile     = protectFile
     91         self.protectFeature  = protectFeature
     92         self.protectProto    = protectProto
     93         self.protectProtoStr = protectProtoStr
     94         self.apicall         = apicall
     95         self.apientry        = apientry
     96         self.apientryp       = apientryp
     97         self.indentFuncProto = indentFuncProto
     98         self.indentFuncPointer = indentFuncPointer
     99         self.alignFuncParam  = alignFuncParam
    100 
    101 # UniqueObjectsOutputGenerator - subclass of OutputGenerator.
    102 # Generates unique objects layer non-dispatchable handle-wrapping code.
    103 #
    104 # ---- methods ----
    105 # UniqueObjectsOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
    106 # ---- methods overriding base class ----
    107 # beginFile(genOpts)
    108 # endFile()
    109 # beginFeature(interface, emit)
    110 # endFeature()
    111 # genCmd(cmdinfo)
    112 # genStruct()
    113 # genType()
    114 class UniqueObjectsOutputGenerator(OutputGenerator):
    115     """Generate UniqueObjects code based on XML element attributes"""
    116     # This is an ordered list of sections in the header file.
    117     ALL_SECTIONS = ['command']
    118     def __init__(self,
    119                  errFile = sys.stderr,
    120                  warnFile = sys.stderr,
    121                  diagFile = sys.stdout):
    122         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    123         self.INDENT_SPACES = 4
    124         self.intercepts = []
    125         self.instance_extensions = []
    126         self.device_extensions = []
    127         # Commands which are not autogenerated but still intercepted
    128         self.no_autogen_list = [
    129             'vkGetDeviceProcAddr',
    130             'vkGetInstanceProcAddr',
    131             'vkCreateInstance',
    132             'vkDestroyInstance',
    133             'vkCreateDevice',
    134             'vkDestroyDevice',
    135             'vkCreateComputePipelines',
    136             'vkCreateGraphicsPipelines',
    137             'vkCreateSwapchainKHR',
    138             'vkCreateSharedSwapchainsKHR',
    139             'vkGetSwapchainImagesKHR',
    140             'vkQueuePresentKHR',
    141             'vkEnumerateInstanceLayerProperties',
    142             'vkEnumerateDeviceLayerProperties',
    143             'vkEnumerateInstanceExtensionProperties',
    144             'vkCreateDescriptorUpdateTemplateKHR',
    145             'vkDestroyDescriptorUpdateTemplateKHR',
    146             'vkUpdateDescriptorSetWithTemplateKHR',
    147             'vkCmdPushDescriptorSetWithTemplateKHR',
    148             'vkDebugMarkerSetObjectTagEXT',
    149             'vkDebugMarkerSetObjectNameEXT',
    150             'vkGetPhysicalDeviceDisplayProperties2KHR',
    151             'vkGetPhysicalDeviceDisplayPlaneProperties2KHR',
    152             'vkGetDisplayModeProperties2KHR',
    153             'vkCreateRenderPass',
    154             'vkDestroyRenderPass',
    155             ]
    156         # Commands shadowed by interface functions and are not implemented
    157         self.interface_functions = [
    158             'vkGetPhysicalDeviceDisplayPropertiesKHR',
    159             'vkGetPhysicalDeviceDisplayPlanePropertiesKHR',
    160             'vkGetDisplayPlaneSupportedDisplaysKHR',
    161             'vkGetDisplayModePropertiesKHR',
    162             'vkGetDisplayPlaneCapabilitiesKHR',
    163             # DebugReport APIs are hooked, but handled separately in the source file
    164             'vkCreateDebugReportCallbackEXT',
    165             'vkDestroyDebugReportCallbackEXT',
    166             'vkDebugReportMessageEXT',
    167             ]
    168         self.headerVersion = None
    169         # Internal state - accumulators for different inner block text
    170         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    171 
    172         self.cmdMembers = []
    173         self.cmd_feature_protect = []  # Save ifdef's for each command
    174         self.cmd_info_data = []        # Save the cmdinfo data for wrapping the handles when processing is complete
    175         self.structMembers = []        # List of StructMemberData records for all Vulkan structs
    176         self.extension_structs = []    # List of all structs or sister-structs containing handles
    177                                        # A sister-struct may contain no handles but shares a structextends attribute with one that does
    178         self.structTypes = dict()      # Map of Vulkan struct typename to required VkStructureType
    179         self.struct_member_dict = dict()
    180         # Named tuples to store struct and command data
    181         self.StructType = namedtuple('StructType', ['name', 'value'])
    182         self.CmdMemberData = namedtuple('CmdMemberData', ['name', 'members'])
    183         self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo'])
    184         self.CmdExtraProtect = namedtuple('CmdExtraProtect', ['name', 'extra_protect'])
    185 
    186         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl', 'islocal', 'iscreate', 'isdestroy', 'feature_protect'])
    187         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
    188     #
    189     def incIndent(self, indent):
    190         inc = ' ' * self.INDENT_SPACES
    191         if indent:
    192             return indent + inc
    193         return inc
    194     #
    195     def decIndent(self, indent):
    196         if indent and (len(indent) > self.INDENT_SPACES):
    197             return indent[:-self.INDENT_SPACES]
    198         return ''
    199     #
    200     # Override makeProtoName to drop the "vk" prefix
    201     def makeProtoName(self, name, tail):
    202         return self.genOpts.apientry + name[2:] + tail
    203     #
    204     # Check if the parameter passed in is a pointer to an array
    205     def paramIsArray(self, param):
    206         return param.attrib.get('len') is not None
    207     #
    208     def beginFile(self, genOpts):
    209         OutputGenerator.beginFile(self, genOpts)
    210         # User-supplied prefix text, if any (list of strings)
    211         if (genOpts.prefixText):
    212             for s in genOpts.prefixText:
    213                 write(s, file=self.outFile)
    214         # Namespace
    215         self.newline()
    216         write('namespace unique_objects {', file = self.outFile)
    217     # Now that the data is all collected and complete, generate and output the wrapping/unwrapping routines
    218     def endFile(self):
    219 
    220         self.struct_member_dict = dict(self.structMembers)
    221 
    222         # Generate the list of APIs that might need to handle wrapped extension structs
    223         self.GenerateCommandWrapExtensionList()
    224         # Write out wrapping/unwrapping functions
    225         self.WrapCommands()
    226         # Build and write out pNext processing function
    227         extension_proc = self.build_extension_processing_func()
    228         self.newline()
    229         write('// Unique Objects pNext extension handling function', file=self.outFile)
    230         write('%s' % extension_proc, file=self.outFile)
    231 
    232         # Actually write the interface to the output file.
    233         if (self.emit):
    234             self.newline()
    235             if (self.featureExtraProtect != None):
    236                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
    237             # Write the unique_objects code to the file
    238             if (self.sections['command']):
    239                 if (self.genOpts.protectProto):
    240                     write(self.genOpts.protectProto,
    241                           self.genOpts.protectProtoStr, file=self.outFile)
    242                 write('\n'.join(self.sections['command']), end=u'', file=self.outFile)
    243             if (self.featureExtraProtect != None):
    244                 write('\n#endif //', self.featureExtraProtect, file=self.outFile)
    245             else:
    246                 self.newline()
    247 
    248         # Record intercepted procedures
    249         write('// Map of all APIs to be intercepted by this layer', file=self.outFile)
    250         write('static const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile)
    251         write('\n'.join(self.intercepts), file=self.outFile)
    252         write('};\n', file=self.outFile)
    253         self.newline()
    254         write('} // namespace unique_objects', file=self.outFile)
    255         # Finish processing in superclass
    256         OutputGenerator.endFile(self)
    257     #
    258     def beginFeature(self, interface, emit):
    259         # Start processing in superclass
    260         OutputGenerator.beginFeature(self, interface, emit)
    261         self.headerVersion = None
    262 
    263         if self.featureName != 'VK_VERSION_1_0':
    264             white_list_entry = []
    265             if (self.featureExtraProtect != None):
    266                 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ]
    267             white_list_entry += [ '"%s"' % self.featureName ]
    268             if (self.featureExtraProtect != None):
    269                 white_list_entry += [ '#endif' ]
    270             featureType = interface.get('type')
    271             if featureType == 'instance':
    272                 self.instance_extensions += white_list_entry
    273             elif featureType == 'device':
    274                 self.device_extensions += white_list_entry
    275     #
    276     def endFeature(self):
    277         # Finish processing in superclass
    278         OutputGenerator.endFeature(self)
    279     #
    280     def genType(self, typeinfo, name):
    281         OutputGenerator.genType(self, typeinfo, name)
    282         typeElem = typeinfo.elem
    283         # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
    284         # Otherwise, emit the tag text.
    285         category = typeElem.get('category')
    286         if (category == 'struct' or category == 'union'):
    287             self.genStruct(typeinfo, name)
    288     #
    289     # Append a definition to the specified section
    290     def appendSection(self, section, text):
    291         # self.sections[section].append('SECTION: ' + section + '\n')
    292         self.sections[section].append(text)
    293     #
    294     # Check if the parameter passed in is a pointer
    295     def paramIsPointer(self, param):
    296         ispointer = False
    297         for elem in param:
    298             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
    299                 ispointer = True
    300         return ispointer
    301     #
    302     # Get the category of a type
    303     def getTypeCategory(self, typename):
    304         types = self.registry.tree.findall("types/type")
    305         for elem in types:
    306             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
    307                 return elem.attrib.get('category')
    308     #
    309     # Check if a parent object is dispatchable or not
    310     def isHandleTypeNonDispatchable(self, handletype):
    311         handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
    312         if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
    313             return True
    314         else:
    315             return False
    316     #
    317     # Retrieve the type and name for a parameter
    318     def getTypeNameTuple(self, param):
    319         type = ''
    320         name = ''
    321         for elem in param:
    322             if elem.tag == 'type':
    323                 type = noneStr(elem.text)
    324             elif elem.tag == 'name':
    325                 name = noneStr(elem.text)
    326         return (type, name)
    327     #
    328     # Retrieve the value of the len tag
    329     def getLen(self, param):
    330         result = None
    331         len = param.attrib.get('len')
    332         if len and len != 'null-terminated':
    333             # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
    334             # have a null terminated array of strings.  We strip the null-terminated from the
    335             # 'len' field and only return the parameter specifying the string count
    336             if 'null-terminated' in len:
    337                 result = len.split(',')[0]
    338             else:
    339                 result = len
    340             # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
    341             result = str(result).replace('::', '->')
    342         return result
    343     #
    344     # Generate a VkStructureType based on a structure typename
    345     def genVkStructureType(self, typename):
    346         # Add underscore between lowercase then uppercase
    347         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
    348         # Change to uppercase
    349         value = value.upper()
    350         # Add STRUCTURE_TYPE_
    351         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
    352     #
    353     # Struct parameter check generation.
    354     # This is a special case of the <type> tag where the contents are interpreted as a set of
    355     # <member> tags instead of freeform C type declarations. The <member> tags are just like
    356     # <param> tags - they are a declaration of a struct or union member. Only simple member
    357     # declarations are supported (no nested structs etc.)
    358     def genStruct(self, typeinfo, typeName):
    359         OutputGenerator.genStruct(self, typeinfo, typeName)
    360         members = typeinfo.elem.findall('.//member')
    361         # Iterate over members once to get length parameters for arrays
    362         lens = set()
    363         for member in members:
    364             len = self.getLen(member)
    365             if len:
    366                 lens.add(len)
    367         # Generate member info
    368         membersInfo = []
    369         for member in members:
    370             # Get the member's type and name
    371             info = self.getTypeNameTuple(member)
    372             type = info[0]
    373             name = info[1]
    374             cdecl = self.makeCParamDecl(member, 0)
    375             # Process VkStructureType
    376             if type == 'VkStructureType':
    377                 # Extract the required struct type value from the comments
    378                 # embedded in the original text defining the 'typeinfo' element
    379                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
    380                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
    381                 if result:
    382                     value = result.group(0)
    383                 else:
    384                     value = self.genVkStructureType(typeName)
    385                 # Store the required type value
    386                 self.structTypes[typeName] = self.StructType(name=name, value=value)
    387             # Store pointer/array/string info
    388             extstructs = self.registry.validextensionstructs[typeName] if name == 'pNext' else None
    389             membersInfo.append(self.CommandParam(type=type,
    390                                                  name=name,
    391                                                  ispointer=self.paramIsPointer(member),
    392                                                  isconst=True if 'const' in cdecl else False,
    393                                                  iscount=True if name in lens else False,
    394                                                  len=self.getLen(member),
    395                                                  extstructs=extstructs,
    396                                                  cdecl=cdecl,
    397                                                  islocal=False,
    398                                                  iscreate=False,
    399                                                  isdestroy=False,
    400                                                  feature_protect=self.featureExtraProtect))
    401         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
    402 
    403     #
    404     # Insert a lock_guard line
    405     def lock_guard(self, indent):
    406         return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
    407     #
    408     # Determine if a struct has an NDO as a member or an embedded member
    409     def struct_contains_ndo(self, struct_item):
    410         struct_member_dict = dict(self.structMembers)
    411         struct_members = struct_member_dict[struct_item]
    412 
    413         for member in struct_members:
    414             if self.isHandleTypeNonDispatchable(member.type):
    415                 return True
    416             elif member.type in struct_member_dict:
    417                 if self.struct_contains_ndo(member.type) == True:
    418                     return True
    419         return False
    420     #
    421     # Return list of struct members which contain, or which sub-structures contain
    422     # an NDO in a given list of parameters or members
    423     def getParmeterStructsWithNdos(self, item_list):
    424         struct_list = set()
    425         for item in item_list:
    426             paramtype = item.find('type')
    427             typecategory = self.getTypeCategory(paramtype.text)
    428             if typecategory == 'struct':
    429                 if self.struct_contains_ndo(paramtype.text) == True:
    430                     struct_list.add(item)
    431         return struct_list
    432     #
    433     # Return list of non-dispatchable objects from a given list of parameters or members
    434     def getNdosInParameterList(self, item_list, create_func):
    435         ndo_list = set()
    436         if create_func == True:
    437             member_list = item_list[0:-1]
    438         else:
    439             member_list = item_list
    440         for item in member_list:
    441             if self.isHandleTypeNonDispatchable(paramtype.text):
    442                 ndo_list.add(item)
    443         return ndo_list
    444     #
    445     # Construct list of extension structs containing handles, or extension structs that share a structextends attribute
    446     # WITH an extension struct containing handles. All extension structs in any pNext chain will have to be copied.
    447     # TODO: make this recursive -- structs buried three or more levels deep are not searched for extensions
    448     def GenerateCommandWrapExtensionList(self):
    449         for struct in self.structMembers:
    450             if (len(struct.members) > 1) and struct.members[1].extstructs is not None:
    451                 found = False;
    452                 for item in struct.members[1].extstructs:
    453                     if item != '' and self.struct_contains_ndo(item) == True:
    454                         found = True
    455                 if found == True:
    456                     for item in struct.members[1].extstructs:
    457                         if item != '' and item not in self.extension_structs:
    458                             self.extension_structs.append(item)
    459     #
    460     # Returns True if a struct may have a pNext chain containing an NDO
    461     def StructWithExtensions(self, struct_type):
    462         if struct_type in self.struct_member_dict:
    463             param_info = self.struct_member_dict[struct_type]
    464             if (len(param_info) > 1) and param_info[1].extstructs is not None:
    465                 for item in param_info[1].extstructs:
    466                     if item in self.extension_structs:
    467                         return True
    468         return False
    469     #
    470     # Generate pNext handling function
    471     def build_extension_processing_func(self):
    472         # Construct helper functions to build and free pNext extension chains
    473         pnext_proc = ''
    474         pnext_proc += 'void *CreateUnwrappedExtensionStructs(layer_data *dev_data, const void *pNext) {\n'
    475         pnext_proc += '    void *cur_pnext = const_cast<void *>(pNext);\n'
    476         pnext_proc += '    void *head_pnext = NULL;\n'
    477         pnext_proc += '    void *prev_ext_struct = NULL;\n'
    478         pnext_proc += '    void *cur_ext_struct = NULL;\n\n'
    479         pnext_proc += '    while (cur_pnext != NULL) {\n'
    480         pnext_proc += '        GenericHeader *header = reinterpret_cast<GenericHeader *>(cur_pnext);\n\n'
    481         pnext_proc += '        switch (header->sType) {\n'
    482         for item in self.extension_structs:
    483             struct_info = self.struct_member_dict[item]
    484             if struct_info[0].feature_protect is not None:
    485                 pnext_proc += '#ifdef %s \n' % struct_info[0].feature_protect
    486             pnext_proc += '            case %s: {\n' % self.structTypes[item].value
    487             pnext_proc += '                    safe_%s *safe_struct = new safe_%s;\n' % (item, item)
    488             pnext_proc += '                    safe_struct->initialize(reinterpret_cast<const %s *>(cur_pnext));\n' % item
    489             # Generate code to unwrap the handles
    490             indent = '                '
    491             (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, 'safe_struct->', 0, False, False, False, False)
    492             pnext_proc += tmp_pre
    493             pnext_proc += '                    cur_ext_struct = reinterpret_cast<void *>(safe_struct);\n'
    494             pnext_proc += '                } break;\n'
    495             if struct_info[0].feature_protect is not None:
    496                 pnext_proc += '#endif // %s \n' % struct_info[0].feature_protect
    497             pnext_proc += '\n'
    498         pnext_proc += '            default:\n'
    499         pnext_proc += '                break;\n'
    500         pnext_proc += '        }\n\n'
    501         pnext_proc += '        // Save pointer to the first structure in the pNext chain\n'
    502         pnext_proc += '        head_pnext = (head_pnext ? head_pnext : cur_ext_struct);\n\n'
    503         pnext_proc += '        // For any extension structure but the first, link the last struct\'s pNext to the current ext struct\n'
    504         pnext_proc += '        if (prev_ext_struct) {\n'
    505         pnext_proc += '            (reinterpret_cast<GenericHeader *>(prev_ext_struct))->pNext = cur_ext_struct;\n'
    506         pnext_proc += '        }\n'
    507         pnext_proc += '        prev_ext_struct = cur_ext_struct;\n\n'
    508         pnext_proc += '        // Process the next structure in the chain\n'
    509         pnext_proc += '        cur_pnext = const_cast<void *>(header->pNext);\n'
    510         pnext_proc += '    }\n'
    511         pnext_proc += '    return head_pnext;\n'
    512         pnext_proc += '}\n\n'
    513         pnext_proc += '// Free a pNext extension chain\n'
    514         pnext_proc += 'void FreeUnwrappedExtensionStructs(void *head) {\n'
    515         pnext_proc += '    void * curr_ptr = head;\n'
    516         pnext_proc += '    while (curr_ptr) {\n'
    517         pnext_proc += '        GenericHeader *header = reinterpret_cast<GenericHeader *>(curr_ptr);\n'
    518         pnext_proc += '        void *temp = curr_ptr;\n'
    519         pnext_proc += '        curr_ptr = header->pNext;\n'
    520         pnext_proc += '        free(temp);\n'
    521         pnext_proc += '    }\n'
    522         pnext_proc += '}\n'
    523         return pnext_proc
    524 
    525     #
    526     # Generate source for creating a non-dispatchable object
    527     def generate_create_ndo_code(self, indent, proto, params, cmd_info):
    528         create_ndo_code = ''
    529         handle_type = params[-1].find('type')
    530         if self.isHandleTypeNonDispatchable(handle_type.text):
    531             # Check for special case where multiple handles are returned
    532             ndo_array = False
    533             if cmd_info[-1].len is not None:
    534                 ndo_array = True;
    535             handle_name = params[-1].find('name')
    536             create_ndo_code += '%sif (VK_SUCCESS == result) {\n' % (indent)
    537             indent = self.incIndent(indent)
    538             create_ndo_code += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent)
    539             ndo_dest = '*%s' % handle_name.text
    540             if ndo_array == True:
    541                 create_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[-1].len)
    542                 indent = self.incIndent(indent)
    543                 ndo_dest = '%s[index0]' % cmd_info[-1].name
    544             create_ndo_code += '%s%s = WrapNew(dev_data, %s);\n' % (indent, ndo_dest, ndo_dest)
    545             if ndo_array == True:
    546                 indent = self.decIndent(indent)
    547                 create_ndo_code += '%s}\n' % indent
    548             indent = self.decIndent(indent)
    549             create_ndo_code += '%s}\n' % (indent)
    550         return create_ndo_code
    551     #
    552     # Generate source for destroying a non-dispatchable object
    553     def generate_destroy_ndo_code(self, indent, proto, cmd_info):
    554         destroy_ndo_code = ''
    555         ndo_array = False
    556         if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]:
    557             # Check for special case where multiple handles are returned
    558             if cmd_info[-1].len is not None:
    559                 ndo_array = True;
    560                 param = -1
    561             else:
    562                 param = -2
    563             if self.isHandleTypeNonDispatchable(cmd_info[param].type) == True:
    564                 if ndo_array == True:
    565                     # This API is freeing an array of handles.  Remove them from the unique_id map.
    566                     destroy_ndo_code += '%sif ((VK_SUCCESS == result) && (%s)) {\n' % (indent, cmd_info[param].name)
    567                     indent = self.incIndent(indent)
    568                     destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent)
    569                     destroy_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[param].len)
    570                     indent = self.incIndent(indent)
    571                     destroy_ndo_code += '%s%s handle = %s[index0];\n' % (indent, cmd_info[param].type, cmd_info[param].name)
    572                     destroy_ndo_code += '%suint64_t unique_id = reinterpret_cast<uint64_t &>(handle);\n' % (indent)
    573                     destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(unique_id);\n' % (indent)
    574                     indent = self.decIndent(indent);
    575                     destroy_ndo_code += '%s}\n' % indent
    576                     indent = self.decIndent(indent);
    577                     destroy_ndo_code += '%s}\n' % indent
    578                 else:
    579                     # Remove a single handle from the map
    580                     destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent)
    581                     destroy_ndo_code += '%suint64_t %s_id = reinterpret_cast<uint64_t &>(%s);\n' % (indent, cmd_info[param].name, cmd_info[param].name)
    582                     destroy_ndo_code += '%s%s = (%s)dev_data->unique_id_mapping[%s_id];\n' % (indent, cmd_info[param].name, cmd_info[param].type, cmd_info[param].name)
    583                     destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(%s_id);\n' % (indent, cmd_info[param].name)
    584                     destroy_ndo_code += '%slock.unlock();\n' % (indent)
    585         return ndo_array, destroy_ndo_code
    586 
    587     #
    588     # Clean up local declarations
    589     def cleanUpLocalDeclarations(self, indent, prefix, name, len, index, process_pnext):
    590         cleanup = '%sif (local_%s%s) {\n' % (indent, prefix, name)
    591         if len is not None:
    592             if process_pnext:
    593                 cleanup += '%s    for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, len, index)
    594                 cleanup += '%s        FreeUnwrappedExtensionStructs(const_cast<void *>(local_%s%s[%s].pNext));\n' % (indent, prefix, name, index)
    595                 cleanup += '%s    }\n' % indent
    596             cleanup += '%s    delete[] local_%s%s;\n' % (indent, prefix, name)
    597         else:
    598             if process_pnext:
    599                 cleanup += '%s    FreeUnwrappedExtensionStructs(const_cast<void *>(local_%s%s->pNext));\n' % (indent, prefix, name)
    600             cleanup += '%s    delete local_%s%s;\n' % (indent, prefix, name)
    601         cleanup += "%s}\n" % (indent)
    602         return cleanup
    603     #
    604     # Output UO code for a single NDO (ndo_count is NULL) or a counted list of NDOs
    605     def outputNDOs(self, ndo_type, ndo_name, ndo_count, prefix, index, indent, destroy_func, destroy_array, top_level):
    606         decl_code = ''
    607         pre_call_code = ''
    608         post_call_code = ''
    609         if ndo_count is not None:
    610             if top_level == True:
    611                 decl_code += '%s%s *local_%s%s = NULL;\n' % (indent, ndo_type, prefix, ndo_name)
    612             pre_call_code += '%s    if (%s%s) {\n' % (indent, prefix, ndo_name)
    613             indent = self.incIndent(indent)
    614             if top_level == True:
    615                 pre_call_code += '%s    local_%s%s = new %s[%s];\n' % (indent, prefix, ndo_name, ndo_type, ndo_count)
    616                 pre_call_code += '%s    for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index)
    617                 indent = self.incIndent(indent)
    618                 pre_call_code += '%s    local_%s%s[%s] = Unwrap(dev_data, %s[%s]);\n' % (indent, prefix, ndo_name, index, ndo_name, index)
    619             else:
    620                 pre_call_code += '%s    for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index)
    621                 indent = self.incIndent(indent)
    622                 pre_call_code += '%s    %s%s[%s] = Unwrap(dev_data, %s%s[%s]);\n' % (indent, prefix, ndo_name, index, prefix, ndo_name, index)
    623             indent = self.decIndent(indent)
    624             pre_call_code += '%s    }\n' % indent
    625             indent = self.decIndent(indent)
    626             pre_call_code += '%s    }\n' % indent
    627             if top_level == True:
    628                 post_call_code += '%sif (local_%s%s)\n' % (indent, prefix, ndo_name)
    629                 indent = self.incIndent(indent)
    630                 post_call_code += '%sdelete[] local_%s;\n' % (indent, ndo_name)
    631         else:
    632             if top_level == True:
    633                 if (destroy_func == False) or (destroy_array == True):
    634                     pre_call_code += '%s    %s = Unwrap(dev_data, %s);\n' % (indent, ndo_name, ndo_name)
    635             else:
    636                 # Make temp copy of this var with the 'local' removed. It may be better to not pass in 'local_'
    637                 # as part of the string and explicitly print it
    638                 fix = str(prefix).strip('local_');
    639                 pre_call_code += '%s    if (%s%s) {\n' % (indent, fix, ndo_name)
    640                 indent = self.incIndent(indent)
    641                 pre_call_code += '%s    %s%s = Unwrap(dev_data, %s%s);\n' % (indent, prefix, ndo_name, fix, ndo_name)
    642                 indent = self.decIndent(indent)
    643                 pre_call_code += '%s    }\n' % indent
    644         return decl_code, pre_call_code, post_call_code
    645     #
    646     # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct
    647     # create_func means that this is API creates or allocates NDOs
    648     # destroy_func indicates that this API destroys or frees NDOs
    649     # destroy_array means that the destroy_func operated on an array of NDOs
    650     def uniquify_members(self, members, indent, prefix, array_index, create_func, destroy_func, destroy_array, first_level_param):
    651         decls = ''
    652         pre_code = ''
    653         post_code = ''
    654         index = 'index%s' % str(array_index)
    655         array_index += 1
    656         # Process any NDOs in this structure and recurse for any sub-structs in this struct
    657         for member in members:
    658             process_pnext = self.StructWithExtensions(member.type)
    659             # Handle NDOs
    660             if self.isHandleTypeNonDispatchable(member.type) == True:
    661                 count_name = member.len
    662                 if (count_name is not None):
    663                     if first_level_param == False:
    664                         count_name = '%s%s' % (prefix, member.len)
    665 
    666                 if (first_level_param == False) or (create_func == False):
    667                     (tmp_decl, tmp_pre, tmp_post) = self.outputNDOs(member.type, member.name, count_name, prefix, index, indent, destroy_func, destroy_array, first_level_param)
    668                     decls += tmp_decl
    669                     pre_code += tmp_pre
    670                     post_code += tmp_post
    671             # Handle Structs that contain NDOs at some level
    672             elif member.type in self.struct_member_dict:
    673                 # Structs at first level will have an NDO, OR, we need a safe_struct for the pnext chain
    674                 if self.struct_contains_ndo(member.type) == True or process_pnext:
    675                     struct_info = self.struct_member_dict[member.type]
    676                     # Struct Array
    677                     if member.len is not None:
    678                         # Update struct prefix
    679                         if first_level_param == True:
    680                             new_prefix = 'local_%s' % member.name
    681                             # Declare safe_VarType for struct
    682                             decls += '%ssafe_%s *%s = NULL;\n' % (indent, member.type, new_prefix)
    683                         else:
    684                             new_prefix = '%s%s' % (prefix, member.name)
    685                         pre_code += '%s    if (%s%s) {\n' % (indent, prefix, member.name)
    686                         indent = self.incIndent(indent)
    687                         if first_level_param == True:
    688                             pre_code += '%s    %s = new safe_%s[%s];\n' % (indent, new_prefix, member.type, member.len)
    689                         pre_code += '%s    for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index)
    690                         indent = self.incIndent(indent)
    691                         if first_level_param == True:
    692                             pre_code += '%s    %s[%s].initialize(&%s[%s]);\n' % (indent, new_prefix, index, member.name, index)
    693                             if process_pnext:
    694                                 pre_code += '%s    %s[%s].pNext = CreateUnwrappedExtensionStructs(dev_data, %s[%s].pNext);\n' % (indent, new_prefix, index, new_prefix, index)
    695                         local_prefix = '%s[%s].' % (new_prefix, index)
    696                         # Process sub-structs in this struct
    697                         (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, local_prefix, array_index, create_func, destroy_func, destroy_array, False)
    698                         decls += tmp_decl
    699                         pre_code += tmp_pre
    700                         post_code += tmp_post
    701                         indent = self.decIndent(indent)
    702                         pre_code += '%s    }\n' % indent
    703                         indent = self.decIndent(indent)
    704                         pre_code += '%s    }\n' % indent
    705                         if first_level_param == True:
    706                             post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len, index, process_pnext)
    707                     # Single Struct
    708                     else:
    709                         # Update struct prefix
    710                         if first_level_param == True:
    711                             new_prefix = 'local_%s->' % member.name
    712                             decls += '%ssafe_%s *local_%s%s = NULL;\n' % (indent, member.type, prefix, member.name)
    713                         else:
    714                             new_prefix = '%s%s->' % (prefix, member.name)
    715                         # Declare safe_VarType for struct
    716                         pre_code += '%s    if (%s%s) {\n' % (indent, prefix, member.name)
    717                         indent = self.incIndent(indent)
    718                         if first_level_param == True:
    719                             pre_code += '%s    local_%s%s = new safe_%s(%s);\n' % (indent, prefix, member.name, member.type, member.name)
    720                         # Process sub-structs in this struct
    721                         (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, new_prefix, array_index, create_func, destroy_func, destroy_array, False)
    722                         decls += tmp_decl
    723                         pre_code += tmp_pre
    724                         post_code += tmp_post
    725                         if process_pnext:
    726                             pre_code += '%s    local_%s%s->pNext = CreateUnwrappedExtensionStructs(dev_data, local_%s%s->pNext);\n' % (indent, prefix, member.name, prefix, member.name)
    727                         indent = self.decIndent(indent)
    728                         pre_code += '%s    }\n' % indent
    729                         if first_level_param == True:
    730                             post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len, index, process_pnext)
    731         return decls, pre_code, post_code
    732     #
    733     # For a particular API, generate the non-dispatchable-object wrapping/unwrapping code
    734     def generate_wrapping_code(self, cmd):
    735         indent = '    '
    736         proto = cmd.find('proto/name')
    737         params = cmd.findall('param')
    738 
    739         if proto.text is not None:
    740             cmd_member_dict = dict(self.cmdMembers)
    741             cmd_info = cmd_member_dict[proto.text]
    742             # Handle ndo create/allocate operations
    743             if cmd_info[0].iscreate:
    744                 create_ndo_code = self.generate_create_ndo_code(indent, proto, params, cmd_info)
    745             else:
    746                 create_ndo_code = ''
    747             # Handle ndo destroy/free operations
    748             if cmd_info[0].isdestroy:
    749                 (destroy_array, destroy_ndo_code) = self.generate_destroy_ndo_code(indent, proto, cmd_info)
    750             else:
    751                 destroy_array = False
    752                 destroy_ndo_code = ''
    753             paramdecl = ''
    754             param_pre_code = ''
    755             param_post_code = ''
    756             create_func = True if create_ndo_code else False
    757             destroy_func = True if destroy_ndo_code else False
    758             (paramdecl, param_pre_code, param_post_code) = self.uniquify_members(cmd_info, indent, '', 0, create_func, destroy_func, destroy_array, True)
    759             param_post_code += create_ndo_code
    760             if destroy_ndo_code:
    761                 if destroy_array == True:
    762                     param_post_code += destroy_ndo_code
    763                 else:
    764                     param_pre_code += destroy_ndo_code
    765             if param_pre_code:
    766                 if (not destroy_func) or (destroy_array):
    767                     param_pre_code = '%s{\n%s%s%s%s}\n' % ('    ', indent, self.lock_guard(indent), param_pre_code, indent)
    768         return paramdecl, param_pre_code, param_post_code
    769     #
    770     # Capture command parameter info needed to wrap NDOs as well as handling some boilerplate code
    771     def genCmd(self, cmdinfo, cmdname):
    772 
    773         # Add struct-member type information to command parameter information
    774         OutputGenerator.genCmd(self, cmdinfo, cmdname)
    775         members = cmdinfo.elem.findall('.//param')
    776         # Iterate over members once to get length parameters for arrays
    777         lens = set()
    778         for member in members:
    779             len = self.getLen(member)
    780             if len:
    781                 lens.add(len)
    782         struct_member_dict = dict(self.structMembers)
    783         # Generate member info
    784         membersInfo = []
    785         constains_extension_structs = False
    786         for member in members:
    787             # Get type and name of member
    788             info = self.getTypeNameTuple(member)
    789             type = info[0]
    790             name = info[1]
    791             cdecl = self.makeCParamDecl(member, 0)
    792             # Check for parameter name in lens set
    793             iscount = True if name in lens else False
    794             len = self.getLen(member)
    795             isconst = True if 'const' in cdecl else False
    796             ispointer = self.paramIsPointer(member)
    797             # Mark param as local if it is an array of NDOs
    798             islocal = False;
    799             if self.isHandleTypeNonDispatchable(type) == True:
    800                 if (len is not None) and (isconst == True):
    801                     islocal = True
    802             # Or if it's a struct that contains an NDO
    803             elif type in struct_member_dict:
    804                 if self.struct_contains_ndo(type) == True:
    805                     islocal = True
    806             isdestroy = True if True in [destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']] else False
    807             iscreate = True if True in [create_txt in cmdname for create_txt in ['Create', 'Allocate', 'GetRandROutputDisplayEXT', 'RegisterDeviceEvent', 'RegisterDisplayEvent']] else False
    808             extstructs = self.registry.validextensionstructs[type] if name == 'pNext' else None
    809             membersInfo.append(self.CommandParam(type=type,
    810                                                  name=name,
    811                                                  ispointer=ispointer,
    812                                                  isconst=isconst,
    813                                                  iscount=iscount,
    814                                                  len=len,
    815                                                  extstructs=extstructs,
    816                                                  cdecl=cdecl,
    817                                                  islocal=islocal,
    818                                                  iscreate=iscreate,
    819                                                  isdestroy=isdestroy,
    820                                                  feature_protect=self.featureExtraProtect))
    821         self.cmdMembers.append(self.CmdMemberData(name=cmdname, members=membersInfo))
    822         self.cmd_info_data.append(self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo))
    823         self.cmd_feature_protect.append(self.CmdExtraProtect(name=cmdname, extra_protect=self.featureExtraProtect))
    824     #
    825     # Create code to wrap NDOs as well as handling some boilerplate code
    826     def WrapCommands(self):
    827         cmd_member_dict = dict(self.cmdMembers)
    828         cmd_info_dict = dict(self.cmd_info_data)
    829         cmd_protect_dict = dict(self.cmd_feature_protect)
    830 
    831         for api_call in self.cmdMembers:
    832             cmdname = api_call.name
    833             cmdinfo = cmd_info_dict[api_call.name]
    834             if cmdname in self.interface_functions:
    835                 continue
    836             if cmdname in self.no_autogen_list:
    837                 decls = self.makeCDecls(cmdinfo.elem)
    838                 self.appendSection('command', '')
    839                 self.appendSection('command', '// Declare only')
    840                 self.appendSection('command', decls[0])
    841                 self.intercepts += [ '    {"%s", (void *)%s},' % (cmdname,cmdname[2:]) ]
    842                 continue
    843             # Generate NDO wrapping/unwrapping code for all parameters
    844             (api_decls, api_pre, api_post) = self.generate_wrapping_code(cmdinfo.elem)
    845             # If API doesn't contain an NDO's, don't fool with it
    846             if not api_decls and not api_pre and not api_post:
    847                 continue
    848             feature_extra_protect = cmd_protect_dict[api_call.name]
    849             if (feature_extra_protect != None):
    850                 self.appendSection('command', '')
    851                 self.appendSection('command', '#ifdef '+ feature_extra_protect)
    852                 self.intercepts += [ '#ifdef %s' % feature_extra_protect ]
    853             # Add intercept to procmap
    854             self.intercepts += [ '    {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ]
    855             decls = self.makeCDecls(cmdinfo.elem)
    856             self.appendSection('command', '')
    857             self.appendSection('command', decls[0][:-1])
    858             self.appendSection('command', '{')
    859             # Setup common to call wrappers, first parameter is always dispatchable
    860             dispatchable_type = cmdinfo.elem.find('param/type').text
    861             dispatchable_name = cmdinfo.elem.find('param/name').text
    862             # Generate local instance/pdev/device data lookup
    863             if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
    864                 self.appendSection('command', '    instance_layer_data *dev_data = GetLayerDataPtr(get_dispatch_key('+dispatchable_name+'), instance_layer_data_map);')
    865             else:
    866                 self.appendSection('command', '    layer_data *dev_data = GetLayerDataPtr(get_dispatch_key('+dispatchable_name+'), layer_data_map);')
    867             # Handle return values, if any
    868             resulttype = cmdinfo.elem.find('proto/type')
    869             if (resulttype != None and resulttype.text == 'void'):
    870               resulttype = None
    871             if (resulttype != None):
    872                 assignresult = resulttype.text + ' result = '
    873             else:
    874                 assignresult = ''
    875             # Pre-pend declarations and pre-api-call codegen
    876             if api_decls:
    877                 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n")))
    878             if api_pre:
    879                 self.appendSection('command', "\n".join(str(api_pre).rstrip().split("\n")))
    880             # Generate the API call itself
    881             # Gather the parameter items
    882             params = cmdinfo.elem.findall('param/name')
    883             # Pull out the text for each of the parameters, separate them by commas in a list
    884             paramstext = ', '.join([str(param.text) for param in params])
    885             # If any of these paramters has been replaced by a local var, fix up the list
    886             params = cmd_member_dict[cmdname]
    887             for param in params:
    888                 if param.islocal == True or self.StructWithExtensions(param.type):
    889                     if param.ispointer == True:
    890                         paramstext = paramstext.replace(param.name, '(%s %s*)local_%s' % ('const', param.type, param.name))
    891                     else:
    892                         paramstext = paramstext.replace(param.name, '(%s %s)local_%s' % ('const', param.type, param.name))
    893             # Use correct dispatch table
    894             API = cmdinfo.elem.attrib.get('name').replace('vk','dev_data->dispatch_table.',1)
    895             # Put all this together for the final down-chain call
    896             self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
    897             # And add the post-API-call codegen
    898             self.appendSection('command', "\n".join(str(api_post).rstrip().split("\n")))
    899             # Handle the return result variable, if any
    900             if (resulttype != None):
    901                 self.appendSection('command', '    return result;')
    902             self.appendSection('command', '}')
    903             if (feature_extra_protect != None):
    904                 self.appendSection('command', '#endif // '+ feature_extra_protect)
    905                 self.intercepts += [ '#endif' ]
    906