Home | History | Annotate | Download | only in scripts
      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 (at] lunarg.com>
     21 
     22 import os,re,sys,string
     23 import xml.etree.ElementTree as etree
     24 from generator import *
     25 from collections import namedtuple
     26 from vuid_mapping import *
     27 
     28 # This is a workaround to use a Python 2.7 and 3.x compatible syntax.
     29 from io import open
     30 
     31 # ObjectTrackerGeneratorOptions - subclass of GeneratorOptions.
     32 #
     33 # Adds options used by ObjectTrackerOutputGenerator objects during
     34 # object_tracker layer generation.
     35 #
     36 # Additional members
     37 #   prefixText - list of strings to prefix generated header with
     38 #     (usually a copyright statement + calling convention macros).
     39 #   protectFile - True if multiple inclusion protection should be
     40 #     generated (based on the filename) around the entire header.
     41 #   protectFeature - True if #ifndef..#endif protection should be
     42 #     generated around a feature interface in the header file.
     43 #   genFuncPointers - True if function pointer typedefs should be
     44 #     generated
     45 #   protectProto - If conditional protection should be generated
     46 #     around prototype declarations, set to either '#ifdef'
     47 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
     48 #     to require opt-out (#ifndef protectProtoStr). Otherwise
     49 #     set to None.
     50 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
     51 #     declarations, if protectProto is set
     52 #   apicall - string to use for the function declaration prefix,
     53 #     such as APICALL on Windows.
     54 #   apientry - string to use for the calling convention macro,
     55 #     in typedefs, such as APIENTRY.
     56 #   apientryp - string to use for the calling convention macro
     57 #     in function pointer typedefs, such as APIENTRYP.
     58 #   indentFuncProto - True if prototype declarations should put each
     59 #     parameter on a separate line
     60 #   indentFuncPointer - True if typedefed function pointers should put each
     61 #     parameter on a separate line
     62 #   alignFuncParam - if nonzero and parameters are being put on a
     63 #     separate line, align parameter names at the specified column
     64 class ObjectTrackerGeneratorOptions(GeneratorOptions):
     65     def __init__(self,
     66                  filename = None,
     67                  directory = '.',
     68                  apiname = None,
     69                  profile = None,
     70                  versions = '.*',
     71                  emitversions = '.*',
     72                  defaultExtensions = None,
     73                  addExtensions = None,
     74                  removeExtensions = None,
     75                  sortProcedure = regSortFeatures,
     76                  prefixText = "",
     77                  genFuncPointers = True,
     78                  protectFile = True,
     79                  protectFeature = True,
     80                  protectProto = None,
     81                  protectProtoStr = None,
     82                  apicall = '',
     83                  apientry = '',
     84                  apientryp = '',
     85                  indentFuncProto = True,
     86                  indentFuncPointer = False,
     87                  alignFuncParam = 0):
     88         GeneratorOptions.__init__(self, filename, directory, apiname, profile,
     89                                   versions, emitversions, defaultExtensions,
     90                                   addExtensions, removeExtensions, sortProcedure)
     91         self.prefixText      = prefixText
     92         self.genFuncPointers = genFuncPointers
     93         self.protectFile     = protectFile
     94         self.protectFeature  = protectFeature
     95         self.protectProto    = protectProto
     96         self.protectProtoStr = protectProtoStr
     97         self.apicall         = apicall
     98         self.apientry        = apientry
     99         self.apientryp       = apientryp
    100         self.indentFuncProto = indentFuncProto
    101         self.indentFuncPointer = indentFuncPointer
    102         self.alignFuncParam  = alignFuncParam
    103 
    104 # ObjectTrackerOutputGenerator - subclass of OutputGenerator.
    105 # Generates object_tracker layer object validation code
    106 #
    107 # ---- methods ----
    108 # ObjectTrackerOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
    109 # ---- methods overriding base class ----
    110 # beginFile(genOpts)
    111 # endFile()
    112 # beginFeature(interface, emit)
    113 # endFeature()
    114 # genCmd(cmdinfo)
    115 # genStruct()
    116 # genType()
    117 class ObjectTrackerOutputGenerator(OutputGenerator):
    118     """Generate ObjectTracker code based on XML element attributes"""
    119     # This is an ordered list of sections in the header file.
    120     ALL_SECTIONS = ['command']
    121     def __init__(self,
    122                  errFile = sys.stderr,
    123                  warnFile = sys.stderr,
    124                  diagFile = sys.stdout):
    125         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    126         self.INDENT_SPACES = 4
    127         self.intercepts = []
    128         self.instance_extensions = []
    129         self.device_extensions = []
    130         # Commands which are not autogenerated but still intercepted
    131         self.no_autogen_list = [
    132             'vkDestroyInstance',
    133             'vkDestroyDevice',
    134             'vkUpdateDescriptorSets',
    135             'vkDestroyDebugReportCallbackEXT',
    136             'vkDebugReportMessageEXT',
    137             'vkGetPhysicalDeviceQueueFamilyProperties',
    138             'vkFreeCommandBuffers',
    139             'vkDestroySwapchainKHR',
    140             'vkDestroyDescriptorPool',
    141             'vkDestroyCommandPool',
    142             'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
    143             'vkResetDescriptorPool',
    144             'vkBeginCommandBuffer',
    145             'vkCreateDebugReportCallbackEXT',
    146             'vkEnumerateInstanceLayerProperties',
    147             'vkEnumerateDeviceLayerProperties',
    148             'vkEnumerateInstanceExtensionProperties',
    149             'vkEnumerateDeviceExtensionProperties',
    150             'vkCreateDevice',
    151             'vkCreateInstance',
    152             'vkEnumeratePhysicalDevices',
    153             'vkAllocateCommandBuffers',
    154             'vkAllocateDescriptorSets',
    155             'vkFreeDescriptorSets',
    156             'vkCmdPushDescriptorSetKHR',
    157             'vkDebugMarkerSetObjectNameEXT',
    158             'vkGetPhysicalDeviceProcAddr',
    159             'vkGetDeviceProcAddr',
    160             'vkGetInstanceProcAddr',
    161             'vkEnumerateInstanceExtensionProperties',
    162             'vkEnumerateInstanceLayerProperties',
    163             'vkEnumerateDeviceLayerProperties',
    164             'vkGetDeviceProcAddr',
    165             'vkGetInstanceProcAddr',
    166             'vkEnumerateDeviceExtensionProperties',
    167             'vk_layerGetPhysicalDeviceProcAddr',
    168             'vkNegotiateLoaderLayerInterfaceVersion',
    169             'vkCreateComputePipelines',
    170             'vkGetDeviceQueue',
    171             'vkGetSwapchainImagesKHR',
    172             'vkCreateDescriptorSetLayout',
    173             ]
    174         # These VUIDS are not implicit, but are best handled in this layer. Codegen for vkDestroy calls will generate a key
    175         # which is translated here into a good VU.  Saves ~40 checks.
    176         self.manual_vuids = dict()
    177         self.manual_vuids = {
    178             "fence-compatalloc": "VALIDATION_ERROR_24e008c2",
    179             "fence-nullalloc": "VALIDATION_ERROR_24e008c4",
    180             "event-compatalloc": "VALIDATION_ERROR_24c008f4",
    181             "event-nullalloc": "VALIDATION_ERROR_24c008f6",
    182             "buffer-compatalloc": "VALIDATION_ERROR_23c00736",
    183             "buffer-nullalloc": "VALIDATION_ERROR_23c00738",
    184             "image-compatalloc": "VALIDATION_ERROR_252007d2",
    185             "image-nullalloc": "VALIDATION_ERROR_252007d4",
    186             "shaderModule-compatalloc": "VALIDATION_ERROR_26a00888",
    187             "shaderModule-nullalloc": "VALIDATION_ERROR_26a0088a",
    188             "pipeline-compatalloc": "VALIDATION_ERROR_25c005fc",
    189             "pipeline-nullalloc": "VALIDATION_ERROR_25c005fe",
    190             "sampler-compatalloc": "VALIDATION_ERROR_26600876",
    191             "sampler-nullalloc": "VALIDATION_ERROR_26600878",
    192             "renderPass-compatalloc": "VALIDATION_ERROR_264006d4",
    193             "renderPass-nullalloc": "VALIDATION_ERROR_264006d6",
    194             "descriptorUpdateTemplate-compatalloc": "VALIDATION_ERROR_248002c8",
    195             "descriptorUpdateTemplate-nullalloc": "VALIDATION_ERROR_248002ca",
    196             "imageView-compatalloc": "VALIDATION_ERROR_25400806",
    197             "imageView-nullalloc": "VALIDATION_ERROR_25400808",
    198             "pipelineCache-compatalloc": "VALIDATION_ERROR_25e00606",
    199             "pipelineCache-nullalloc": "VALIDATION_ERROR_25e00608",
    200             "pipelineLayout-compatalloc": "VALIDATION_ERROR_26000256",
    201             "pipelineLayout-nullalloc": "VALIDATION_ERROR_26000258",
    202             "descriptorSetLayout-compatalloc": "VALIDATION_ERROR_24600238",
    203             "descriptorSetLayout-nullalloc": "VALIDATION_ERROR_2460023a",
    204             "semaphore-compatalloc": "VALIDATION_ERROR_268008e4",
    205             "semaphore-nullalloc": "VALIDATION_ERROR_268008e6",
    206             "queryPool-compatalloc": "VALIDATION_ERROR_26200634",
    207             "queryPool-nullalloc": "VALIDATION_ERROR_26200636",
    208             "bufferView-compatalloc": "VALIDATION_ERROR_23e00752",
    209             "bufferView-nullalloc": "VALIDATION_ERROR_23e00754",
    210             "surface-compatalloc": "VALIDATION_ERROR_26c009e6",
    211             "surface-nullalloc": "VALIDATION_ERROR_26c009e8",
    212             "framebuffer-compatalloc": "VALIDATION_ERROR_250006fa",
    213             "framebuffer-nullalloc": "VALIDATION_ERROR_250006fc",
    214            }
    215 
    216         # Commands shadowed by interface functions and are not implemented
    217         self.interface_functions = [
    218             ]
    219         self.headerVersion = None
    220         # Internal state - accumulators for different inner block text
    221         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    222         self.cmdMembers = []
    223         self.cmd_feature_protect = []  # Save ifdef's for each command
    224         self.cmd_info_data = []        # Save the cmdinfo data for validating the handles when processing is complete
    225         self.structMembers = []        # List of StructMemberData records for all Vulkan structs
    226         self.extension_structs = []    # List of all structs or sister-structs containing handles
    227                                        # A sister-struct may contain no handles but shares <validextensionstructs> with one that does
    228         self.structTypes = dict()      # Map of Vulkan struct typename to required VkStructureType
    229         self.struct_member_dict = dict()
    230         # Named tuples to store struct and command data
    231         self.StructType = namedtuple('StructType', ['name', 'value'])
    232         self.CmdMemberData = namedtuple('CmdMemberData', ['name', 'members'])
    233         self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo'])
    234         self.CmdExtraProtect = namedtuple('CmdExtraProtect', ['name', 'extra_protect'])
    235         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'cdecl', 'islocal', 'iscreate', 'isdestroy', 'feature_protect'])
    236         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
    237         self.object_types = []         # List of all handle types
    238         self.valid_vuids = set()       # Set of all valid VUIDs
    239         self.vuid_file = None
    240         # Cover cases where file is built from scripts directory, Lin/Win, or Android build structure
    241         # Set cwd to the script directory to more easily locate the header.
    242         previous_dir = os.getcwd()
    243         os.chdir(os.path.dirname(sys.argv[0]))
    244         vuid_filename_locations = [
    245             './vk_validation_error_messages.h',
    246             '../layers/vk_validation_error_messages.h',
    247             '../../layers/vk_validation_error_messages.h',
    248             '../../../layers/vk_validation_error_messages.h',
    249             ]
    250         for vuid_filename in vuid_filename_locations:
    251             if os.path.isfile(vuid_filename):
    252                 self.vuid_file = open(vuid_filename, "r", encoding="utf8")
    253                 break
    254         if self.vuid_file == None:
    255             print("Error: Could not find vk_validation_error_messages.h")
    256             sys.exit(1)
    257         os.chdir(previous_dir)
    258     #
    259     # Check if the parameter passed in is optional
    260     def paramIsOptional(self, param):
    261         # See if the handle is optional
    262         isoptional = False
    263         # Simple, if it's optional, return true
    264         optString = param.attrib.get('optional')
    265         if optString:
    266             if optString == 'true':
    267                 isoptional = True
    268             elif ',' in optString:
    269                 opts = []
    270                 for opt in optString.split(','):
    271                     val = opt.strip()
    272                     if val == 'true':
    273                         opts.append(True)
    274                     elif val == 'false':
    275                         opts.append(False)
    276                     else:
    277                         print('Unrecognized len attribute value',val)
    278                 isoptional = opts
    279         return isoptional
    280     #
    281     # Convert decimal number to 8 digit hexadecimal lower-case representation
    282     def IdToHex(self, dec_num):
    283         if dec_num > 4294967295:
    284             print ("ERROR: Decimal # %d can't be represented in 8 hex digits" % (dec_num))
    285             sys.exit()
    286         hex_num = hex(dec_num)
    287         return hex_num[2:].zfill(8)
    288     #
    289     # Get VUID identifier from implicit VUID tag
    290     def GetVuid(self, vuid_string):
    291         if '->' in vuid_string:
    292            return "VALIDATION_ERROR_UNDEFINED"
    293         vuid_num = self.IdToHex(convertVUID(vuid_string))
    294         if vuid_num in self.valid_vuids:
    295             vuid = "VALIDATION_ERROR_%s" % vuid_num
    296         else:
    297             vuid = "VALIDATION_ERROR_UNDEFINED"
    298         return vuid
    299     #
    300     # Increases indent by 4 spaces and tracks it globally
    301     def incIndent(self, indent):
    302         inc = ' ' * self.INDENT_SPACES
    303         if indent:
    304             return indent + inc
    305         return inc
    306     #
    307     # Decreases indent by 4 spaces and tracks it globally
    308     def decIndent(self, indent):
    309         if indent and (len(indent) > self.INDENT_SPACES):
    310             return indent[:-self.INDENT_SPACES]
    311         return ''
    312     #
    313     # Override makeProtoName to drop the "vk" prefix
    314     def makeProtoName(self, name, tail):
    315         return self.genOpts.apientry + name[2:] + tail
    316     #
    317     # Check if the parameter passed in is a pointer to an array
    318     def paramIsArray(self, param):
    319         return param.attrib.get('len') is not None
    320     #
    321     # Generate the object tracker undestroyed object validation function
    322     def GenReportFunc(self):
    323         output_func = ''
    324         output_func += 'void ReportUndestroyedObjects(VkDevice device, enum UNIQUE_VALIDATION_ERROR_CODE error_code) {\n'
    325         output_func += '    DeviceReportUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer, error_code);\n'
    326         for handle in self.object_types:
    327             if self.isHandleTypeNonDispatchable(handle):
    328                 output_func += '    DeviceReportUndestroyedObjects(device, %s, error_code);\n' % (self.GetVulkanObjType(handle))
    329         output_func += '}\n'
    330         return output_func
    331     #
    332     # Called at beginning of processing as file is opened
    333     def beginFile(self, genOpts):
    334         OutputGenerator.beginFile(self, genOpts)
    335         # Open vk_validation_error_messages.h file to verify computed VUIDs
    336         for line in self.vuid_file:
    337             # Grab hex number from enum definition
    338             vuid_list = line.split('0x')
    339             # If this is a valid enumeration line, remove trailing comma and CR
    340             if len(vuid_list) == 2:
    341                 vuid_num = vuid_list[1][:-2]
    342                 # Make sure this is a good hex number before adding to set
    343                 if len(vuid_num) == 8 and all(c in string.hexdigits for c in vuid_num):
    344                     self.valid_vuids.add(vuid_num)
    345         # File Comment
    346         file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
    347         file_comment += '// See object_tracker_generator.py for modifications\n'
    348         write(file_comment, file=self.outFile)
    349         # Copyright Statement
    350         copyright = ''
    351         copyright += '\n'
    352         copyright += '/***************************************************************************\n'
    353         copyright += ' *\n'
    354         copyright += ' * Copyright (c) 2015-2017 The Khronos Group Inc.\n'
    355         copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
    356         copyright += ' * Copyright (c) 2015-2017 LunarG, Inc.\n'
    357         copyright += ' * Copyright (c) 2015-2017 Google Inc.\n'
    358         copyright += ' *\n'
    359         copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
    360         copyright += ' * you may not use this file except in compliance with the License.\n'
    361         copyright += ' * You may obtain a copy of the License at\n'
    362         copyright += ' *\n'
    363         copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
    364         copyright += ' *\n'
    365         copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
    366         copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
    367         copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
    368         copyright += ' * See the License for the specific language governing permissions and\n'
    369         copyright += ' * limitations under the License.\n'
    370         copyright += ' *\n'
    371         copyright += ' * Author: Mark Lobodzinski <mark (at] lunarg.com>\n'
    372         copyright += ' *\n'
    373         copyright += ' ****************************************************************************/\n'
    374         write(copyright, file=self.outFile)
    375         # Namespace
    376         self.newline()
    377         write('#include "object_tracker.h"', file = self.outFile)
    378         self.newline()
    379         write('namespace object_tracker {', file = self.outFile)
    380     #
    381     # Now that the data is all collected and complete, generate and output the object validation routines
    382     def endFile(self):
    383         self.struct_member_dict = dict(self.structMembers)
    384         # Generate the list of APIs that might need to handle wrapped extension structs
    385         # self.GenerateCommandWrapExtensionList()
    386         self.WrapCommands()
    387         # Build undestroyed objects reporting function
    388         report_func = self.GenReportFunc()
    389         self.newline()
    390         write('// ObjectTracker undestroyed objects validation function', file=self.outFile)
    391         write('%s' % report_func, file=self.outFile)
    392         # Actually write the interface to the output file.
    393         if (self.emit):
    394             self.newline()
    395             if (self.featureExtraProtect != None):
    396                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
    397             # Write the object_tracker code to the file
    398             if (self.sections['command']):
    399                 if (self.genOpts.protectProto):
    400                     write(self.genOpts.protectProto,
    401                           self.genOpts.protectProtoStr, file=self.outFile)
    402                 write('\n'.join(self.sections['command']), end=u'', file=self.outFile)
    403             if (self.featureExtraProtect != None):
    404                 write('\n#endif //', self.featureExtraProtect, file=self.outFile)
    405             else:
    406                 self.newline()
    407 
    408         # Record intercepted procedures
    409         write('// Map of all APIs to be intercepted by this layer', file=self.outFile)
    410         write('const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile)
    411         write('\n'.join(self.intercepts), file=self.outFile)
    412         write('};\n', file=self.outFile)
    413         self.newline()
    414         write('} // namespace object_tracker', file=self.outFile)
    415         # Finish processing in superclass
    416         OutputGenerator.endFile(self)
    417     #
    418     # Processing point at beginning of each extension definition
    419     def beginFeature(self, interface, emit):
    420         # Start processing in superclass
    421         OutputGenerator.beginFeature(self, interface, emit)
    422         self.headerVersion = None
    423 
    424         if self.featureName != 'VK_VERSION_1_0':
    425             white_list_entry = []
    426             if (self.featureExtraProtect != None):
    427                 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ]
    428             white_list_entry += [ '"%s"' % self.featureName ]
    429             if (self.featureExtraProtect != None):
    430                 white_list_entry += [ '#endif' ]
    431             featureType = interface.get('type')
    432             if featureType == 'instance':
    433                 self.instance_extensions += white_list_entry
    434             elif featureType == 'device':
    435                 self.device_extensions += white_list_entry
    436     #
    437     # Processing point at end of each extension definition
    438     def endFeature(self):
    439         # Finish processing in superclass
    440         OutputGenerator.endFeature(self)
    441     #
    442     # Process enums, structs, etc.
    443     def genType(self, typeinfo, name):
    444         OutputGenerator.genType(self, typeinfo, name)
    445         typeElem = typeinfo.elem
    446         # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
    447         # Otherwise, emit the tag text.
    448         category = typeElem.get('category')
    449         if (category == 'struct' or category == 'union'):
    450             self.genStruct(typeinfo, name)
    451         if category == 'handle':
    452             self.object_types.append(name)
    453     #
    454     # Append a definition to the specified section
    455     def appendSection(self, section, text):
    456         # self.sections[section].append('SECTION: ' + section + '\n')
    457         self.sections[section].append(text)
    458     #
    459     # Check if the parameter passed in is a pointer
    460     def paramIsPointer(self, param):
    461         ispointer = False
    462         for elem in param:
    463             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
    464                 ispointer = True
    465         return ispointer
    466     #
    467     # Get the category of a type
    468     def getTypeCategory(self, typename):
    469         types = self.registry.tree.findall("types/type")
    470         for elem in types:
    471             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
    472                 return elem.attrib.get('category')
    473     #
    474     # Check if a parent object is dispatchable or not
    475     def isHandleTypeObject(self, handletype):
    476         handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
    477         if handle is not None:
    478             return True
    479         else:
    480             return False
    481     #
    482     # Check if a parent object is dispatchable or not
    483     def isHandleTypeNonDispatchable(self, handletype):
    484         handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
    485         if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
    486             return True
    487         else:
    488             return False
    489     #
    490     # Retrieve the type and name for a parameter
    491     def getTypeNameTuple(self, param):
    492         type = ''
    493         name = ''
    494         for elem in param:
    495             if elem.tag == 'type':
    496                 type = noneStr(elem.text)
    497             elif elem.tag == 'name':
    498                 name = noneStr(elem.text)
    499         return (type, name)
    500     #
    501     # Retrieve the value of the len tag
    502     def getLen(self, param):
    503         result = None
    504         len = param.attrib.get('len')
    505         if len and len != 'null-terminated':
    506             # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
    507             # have a null terminated array of strings.  We strip the null-terminated from the
    508             # 'len' field and only return the parameter specifying the string count
    509             if 'null-terminated' in len:
    510                 result = len.split(',')[0]
    511             else:
    512                 result = len
    513             # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
    514             result = str(result).replace('::', '->')
    515         return result
    516     #
    517     # Generate a VkStructureType based on a structure typename
    518     def genVkStructureType(self, typename):
    519         # Add underscore between lowercase then uppercase
    520         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
    521         # Change to uppercase
    522         value = value.upper()
    523         # Add STRUCTURE_TYPE_
    524         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
    525     #
    526     # Struct parameter check generation.
    527     # This is a special case of the <type> tag where the contents are interpreted as a set of
    528     # <member> tags instead of freeform C type declarations. The <member> tags are just like
    529     # <param> tags - they are a declaration of a struct or union member. Only simple member
    530     # declarations are supported (no nested structs etc.)
    531     def genStruct(self, typeinfo, typeName):
    532         OutputGenerator.genStruct(self, typeinfo, typeName)
    533         members = typeinfo.elem.findall('.//member')
    534         # Iterate over members once to get length parameters for arrays
    535         lens = set()
    536         for member in members:
    537             len = self.getLen(member)
    538             if len:
    539                 lens.add(len)
    540         # Generate member info
    541         membersInfo = []
    542         for member in members:
    543             # Get the member's type and name
    544             info = self.getTypeNameTuple(member)
    545             type = info[0]
    546             name = info[1]
    547             cdecl = self.makeCParamDecl(member, 0)
    548             # Process VkStructureType
    549             if type == 'VkStructureType':
    550                 # Extract the required struct type value from the comments
    551                 # embedded in the original text defining the 'typeinfo' element
    552                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
    553                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
    554                 if result:
    555                     value = result.group(0)
    556                 else:
    557                     value = self.genVkStructureType(typeName)
    558                 # Store the required type value
    559                 self.structTypes[typeName] = self.StructType(name=name, value=value)
    560             # Store pointer/array/string info
    561             extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
    562             membersInfo.append(self.CommandParam(type=type,
    563                                                  name=name,
    564                                                  ispointer=self.paramIsPointer(member),
    565                                                  isconst=True if 'const' in cdecl else False,
    566                                                  isoptional=self.paramIsOptional(member),
    567                                                  iscount=True if name in lens else False,
    568                                                  len=self.getLen(member),
    569                                                  extstructs=extstructs,
    570                                                  cdecl=cdecl,
    571                                                  islocal=False,
    572                                                  iscreate=False,
    573                                                  isdestroy=False,
    574                                                  feature_protect=self.featureExtraProtect))
    575         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
    576     #
    577     # Insert a lock_guard line
    578     def lock_guard(self, indent):
    579         return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
    580     #
    581     # Determine if a struct has an object as a member or an embedded member
    582     def struct_contains_object(self, struct_item):
    583         struct_member_dict = dict(self.structMembers)
    584         struct_members = struct_member_dict[struct_item]
    585 
    586         for member in struct_members:
    587             if self.isHandleTypeObject(member.type):
    588                 return True
    589             elif member.type in struct_member_dict:
    590                 if self.struct_contains_object(member.type) == True:
    591                     return True
    592         return False
    593     #
    594     # Return list of struct members which contain, or whose sub-structures contain an obj in a given list of parameters or members
    595     def getParmeterStructsWithObjects(self, item_list):
    596         struct_list = set()
    597         for item in item_list:
    598             paramtype = item.find('type')
    599             typecategory = self.getTypeCategory(paramtype.text)
    600             if typecategory == 'struct':
    601                 if self.struct_contains_object(paramtype.text) == True:
    602                     struct_list.add(item)
    603         return struct_list
    604     #
    605     # Return list of objects from a given list of parameters or members
    606     def getObjectsInParameterList(self, item_list, create_func):
    607         object_list = set()
    608         if create_func == True:
    609             member_list = item_list[0:-1]
    610         else:
    611             member_list = item_list
    612         for item in member_list:
    613             if self.isHandleTypeObject(paramtype.text):
    614                 object_list.add(item)
    615         return object_list
    616     #
    617     # Construct list of extension structs containing handles, or extension structs that share a <validextensionstructs>
    618     # tag WITH an extension struct containing handles. 
    619     def GenerateCommandWrapExtensionList(self):
    620         for struct in self.structMembers:
    621             if (len(struct.members) > 1) and struct.members[1].extstructs is not None:
    622                 found = False;
    623                 for item in struct.members[1].extstructs.split(','):
    624                     if item != '' and self.struct_contains_object(item) == True:
    625                         found = True
    626                 if found == True:
    627                     for item in struct.members[1].extstructs.split(','):
    628                         if item != '' and item not in self.extension_structs:
    629                             self.extension_structs.append(item)
    630     #
    631     # Returns True if a struct may have a pNext chain containing an object
    632     def StructWithExtensions(self, struct_type):
    633         if struct_type in self.struct_member_dict:
    634             param_info = self.struct_member_dict[struct_type]
    635             if (len(param_info) > 1) and param_info[1].extstructs is not None:
    636                 for item in param_info[1].extstructs.split(','):
    637                     if item in self.extension_structs:
    638                         return True
    639         return False
    640     #
    641     # Generate VulkanObjectType from object type
    642     def GetVulkanObjType(self, type):
    643         return 'kVulkanObjectType%s' % type[2:]
    644     #
    645     # Return correct dispatch table type -- instance or device
    646     def GetDispType(self, type):
    647         return 'instance' if type in ['VkInstance', 'VkPhysicalDevice'] else 'device'
    648     #
    649     # Generate source for creating a Vulkan object
    650     def generate_create_object_code(self, indent, proto, params, cmd_info):
    651         create_obj_code = ''
    652         handle_type = params[-1].find('type')
    653         if self.isHandleTypeObject(handle_type.text):
    654             # Check for special case where multiple handles are returned
    655             object_array = False
    656             if cmd_info[-1].len is not None:
    657                 object_array = True;
    658             handle_name = params[-1].find('name')
    659             create_obj_code += '%sif (VK_SUCCESS == result) {\n' % (indent)
    660             indent = self.incIndent(indent)
    661             create_obj_code += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent)
    662             object_dest = '*%s' % handle_name.text
    663             if object_array == True:
    664                 create_obj_code += '%sfor (uint32_t index = 0; index < %s; index++) {\n' % (indent, cmd_info[-1].len)
    665                 indent = self.incIndent(indent)
    666                 object_dest = '%s[index]' % cmd_info[-1].name
    667             create_obj_code += '%sCreateObject(%s, %s, %s, pAllocator);\n' % (indent, params[0].find('name').text, object_dest, self.GetVulkanObjType(cmd_info[-1].type))
    668             if object_array == True:
    669                 indent = self.decIndent(indent)
    670                 create_obj_code += '%s}\n' % indent
    671             indent = self.decIndent(indent)
    672             create_obj_code += '%s}\n' % (indent)
    673         return create_obj_code
    674     #
    675     # Generate source for destroying a non-dispatchable object
    676     def generate_destroy_object_code(self, indent, proto, cmd_info):
    677         destroy_obj_code = ''
    678         object_array = False
    679         if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]:
    680             # Check for special case where multiple handles are returned
    681             if cmd_info[-1].len is not None:
    682                 object_array = True;
    683                 param = -1
    684             else:
    685                 param = -2
    686             compatalloc_vuid_string = '%s-compatalloc' % cmd_info[param].name
    687             nullalloc_vuid_string = '%s-nullalloc' % cmd_info[param].name
    688             compatalloc_vuid = self.manual_vuids.get(compatalloc_vuid_string, "VALIDATION_ERROR_UNDEFINED")
    689             nullalloc_vuid = self.manual_vuids.get(nullalloc_vuid_string, "VALIDATION_ERROR_UNDEFINED")
    690             if self.isHandleTypeObject(cmd_info[param].type) == True:
    691                 if object_array == True:
    692                     # This API is freeing an array of handles -- add loop control
    693                     destroy_obj_code += 'HEY, NEED TO DESTROY AN ARRAY\n'
    694                 else:
    695                     # Call Destroy a single time
    696                     destroy_obj_code += '%sif (skip) return;\n' % indent
    697                     destroy_obj_code += '%s{\n' % indent
    698                     destroy_obj_code += '%s    std::lock_guard<std::mutex> lock(global_lock);\n' % indent
    699                     destroy_obj_code += '%s    DestroyObject(%s, %s, %s, pAllocator, %s, %s);\n' % (indent, cmd_info[0].name, cmd_info[param].name, self.GetVulkanObjType(cmd_info[param].type), compatalloc_vuid, nullalloc_vuid)
    700                     destroy_obj_code += '%s}\n' % indent
    701         return object_array, destroy_obj_code
    702     #
    703     # Output validation for a single object (obj_count is NULL) or a counted list of objects
    704     def outputObjects(self, obj_type, obj_name, obj_count, prefix, index, indent, destroy_func, destroy_array, disp_name, parent_name, null_allowed, top_level):
    705         decl_code = ''
    706         pre_call_code = ''
    707         post_call_code = ''
    708         param_vuid_string = 'VUID-%s-%s-parameter' % (parent_name, obj_name)
    709         parent_vuid_string = 'VUID-%s-%s-parent' % (parent_name, obj_name)
    710         param_vuid = self.GetVuid(param_vuid_string)
    711         parent_vuid = self.GetVuid(parent_vuid_string)
    712         # If no parent VUID for this member, look for a commonparent VUID
    713         if parent_vuid == 'VALIDATION_ERROR_UNDEFINED':
    714             commonparent_vuid_string = 'VUID-%s-commonparent' % parent_name
    715             parent_vuid = self.GetVuid(commonparent_vuid_string)
    716         if obj_count is not None:
    717             pre_call_code += '%s    if (%s%s) {\n' % (indent, prefix, obj_name)
    718             indent = self.incIndent(indent)
    719             pre_call_code += '%s    for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, obj_count, index)
    720             indent = self.incIndent(indent)
    721             pre_call_code += '%s    skip |= ValidateObject(%s, %s%s[%s], %s, %s, %s, %s);\n' % (indent, disp_name, prefix, obj_name, index, self.GetVulkanObjType(obj_type), null_allowed, param_vuid, parent_vuid)
    722             indent = self.decIndent(indent)
    723             pre_call_code += '%s    }\n' % indent
    724             indent = self.decIndent(indent)
    725             pre_call_code += '%s    }\n' % indent
    726         else:
    727             pre_call_code += '%s    skip |= ValidateObject(%s, %s%s, %s, %s, %s, %s);\n' % (indent, disp_name, prefix, obj_name, self.GetVulkanObjType(obj_type), null_allowed, param_vuid, parent_vuid)
    728         return decl_code, pre_call_code, post_call_code
    729     #
    730     # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct
    731     # create_func means that this is API creates or allocates objects
    732     # destroy_func indicates that this API destroys or frees objects
    733     # destroy_array means that the destroy_func operated on an array of objects
    734     def validate_objects(self, members, indent, prefix, array_index, create_func, destroy_func, destroy_array, disp_name, parent_name, first_level_param):
    735         decls = ''
    736         pre_code = ''
    737         post_code = ''
    738         index = 'index%s' % str(array_index)
    739         array_index += 1
    740         # Process any objects in this structure and recurse for any sub-structs in this struct
    741         for member in members:
    742             # Handle objects
    743             if member.iscreate and first_level_param and member == members[-1]:
    744                 continue
    745             if self.isHandleTypeObject(member.type) == True:
    746                 count_name = member.len
    747                 if (count_name is not None):
    748                     count_name = '%s%s' % (prefix, member.len)
    749                 null_allowed = member.isoptional
    750                 (tmp_decl, tmp_pre, tmp_post) = self.outputObjects(member.type, member.name, count_name, prefix, index, indent, destroy_func, destroy_array, disp_name, parent_name, str(null_allowed).lower(), first_level_param)
    751                 decls += tmp_decl
    752                 pre_code += tmp_pre
    753                 post_code += tmp_post
    754             # Handle Structs that contain objects at some level
    755             elif member.type in self.struct_member_dict:
    756                 # Structs at first level will have an object
    757                 if self.struct_contains_object(member.type) == True:
    758                     struct_info = self.struct_member_dict[member.type]
    759                     # Struct Array
    760                     if member.len is not None:
    761                         # Update struct prefix
    762                         new_prefix = '%s%s' % (prefix, member.name)
    763                         pre_code += '%s    if (%s%s) {\n' % (indent, prefix, member.name)
    764                         indent = self.incIndent(indent)
    765                         pre_code += '%s    for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index)
    766                         indent = self.incIndent(indent)
    767                         local_prefix = '%s[%s].' % (new_prefix, index)
    768                         # Process sub-structs in this struct
    769                         (tmp_decl, tmp_pre, tmp_post) = self.validate_objects(struct_info, indent, local_prefix, array_index, create_func, destroy_func, destroy_array, disp_name, member.type, False)
    770                         decls += tmp_decl
    771                         pre_code += tmp_pre
    772                         post_code += tmp_post
    773                         indent = self.decIndent(indent)
    774                         pre_code += '%s    }\n' % indent
    775                         indent = self.decIndent(indent)
    776                         pre_code += '%s    }\n' % indent
    777                     # Single Struct
    778                     else:
    779                         # Update struct prefix
    780                         new_prefix = '%s%s->' % (prefix, member.name)
    781                         # Declare safe_VarType for struct
    782                         pre_code += '%s    if (%s%s) {\n' % (indent, prefix, member.name)
    783                         indent = self.incIndent(indent)
    784                         # Process sub-structs in this struct
    785                         (tmp_decl, tmp_pre, tmp_post) = self.validate_objects(struct_info, indent, new_prefix, array_index, create_func, destroy_func, destroy_array, disp_name, member.type, False)
    786                         decls += tmp_decl
    787                         pre_code += tmp_pre
    788                         post_code += tmp_post
    789                         indent = self.decIndent(indent)
    790                         pre_code += '%s    }\n' % indent
    791         return decls, pre_code, post_code
    792     #
    793     # For a particular API, generate the object handling code
    794     def generate_wrapping_code(self, cmd):
    795         indent = '    '
    796         proto = cmd.find('proto/name')
    797         params = cmd.findall('param')
    798         if proto.text is not None:
    799             cmd_member_dict = dict(self.cmdMembers)
    800             cmd_info = cmd_member_dict[proto.text]
    801             disp_name = cmd_info[0].name
    802             # Handle object create operations
    803             if cmd_info[0].iscreate:
    804                 create_obj_code = self.generate_create_object_code(indent, proto, params, cmd_info)
    805             else:
    806                 create_obj_code = ''
    807             # Handle object destroy operations
    808             if cmd_info[0].isdestroy:
    809                 (destroy_array, destroy_object_code) = self.generate_destroy_object_code(indent, proto, cmd_info)
    810             else:
    811                 destroy_array = False
    812                 destroy_object_code = ''
    813             paramdecl = ''
    814             param_pre_code = ''
    815             param_post_code = ''
    816             create_func = True if create_obj_code else False
    817             destroy_func = True if destroy_object_code else False
    818             (paramdecl, param_pre_code, param_post_code) = self.validate_objects(cmd_info, indent, '', 0, create_func, destroy_func, destroy_array, disp_name, proto.text, True)
    819             param_post_code += create_obj_code
    820             if destroy_object_code:
    821                 if destroy_array == True:
    822                     param_post_code += destroy_object_code
    823                 else:
    824                     param_pre_code += destroy_object_code
    825             if param_pre_code:
    826                 if (not destroy_func) or (destroy_array):
    827                     param_pre_code = '%s{\n%s%s%s%s}\n' % ('    ', indent, self.lock_guard(indent), param_pre_code, indent)
    828         return paramdecl, param_pre_code, param_post_code
    829     #
    830     # Capture command parameter info needed to create, destroy, and validate objects
    831     def genCmd(self, cmdinfo, cmdname):
    832 
    833         # Add struct-member type information to command parameter information
    834         OutputGenerator.genCmd(self, cmdinfo, cmdname)
    835         members = cmdinfo.elem.findall('.//param')
    836         # Iterate over members once to get length parameters for arrays
    837         lens = set()
    838         for member in members:
    839             len = self.getLen(member)
    840             if len:
    841                 lens.add(len)
    842         struct_member_dict = dict(self.structMembers)
    843         # Generate member info
    844         membersInfo = []
    845         constains_extension_structs = False
    846         for member in members:
    847             # Get type and name of member
    848             info = self.getTypeNameTuple(member)
    849             type = info[0]
    850             name = info[1]
    851             cdecl = self.makeCParamDecl(member, 0)
    852             # Check for parameter name in lens set
    853             iscount = True if name in lens else False
    854             len = self.getLen(member)
    855             isconst = True if 'const' in cdecl else False
    856             ispointer = self.paramIsPointer(member)
    857             # Mark param as local if it is an array of objects
    858             islocal = False;
    859             if self.isHandleTypeObject(type) == True:
    860                 if (len is not None) and (isconst == True):
    861                     islocal = True
    862             # Or if it's a struct that contains an object
    863             elif type in struct_member_dict:
    864                 if self.struct_contains_object(type) == True:
    865                     islocal = True
    866             isdestroy = True if True in [destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']] else False
    867             iscreate = True if True in [create_txt in cmdname for create_txt in ['Create', 'Allocate', 'Enumerate', 'RegisterDeviceEvent', 'RegisterDisplayEvent']] or ('vkGet' in cmdname and member == members[-1] and ispointer == True)  else False
    868             extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
    869             membersInfo.append(self.CommandParam(type=type,
    870                                                  name=name,
    871                                                  ispointer=ispointer,
    872                                                  isconst=isconst,
    873                                                  isoptional=self.paramIsOptional(member),
    874                                                  iscount=iscount,
    875                                                  len=len,
    876                                                  extstructs=extstructs,
    877                                                  cdecl=cdecl,
    878                                                  islocal=islocal,
    879                                                  iscreate=iscreate,
    880                                                  isdestroy=isdestroy,
    881                                                  feature_protect=self.featureExtraProtect))
    882         self.cmdMembers.append(self.CmdMemberData(name=cmdname, members=membersInfo))
    883         self.cmd_info_data.append(self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo))
    884         self.cmd_feature_protect.append(self.CmdExtraProtect(name=cmdname, extra_protect=self.featureExtraProtect))
    885     #
    886     # Create code Create, Destroy, and validate Vulkan objects
    887     def WrapCommands(self):
    888         cmd_member_dict = dict(self.cmdMembers)
    889         cmd_info_dict = dict(self.cmd_info_data)
    890         cmd_protect_dict = dict(self.cmd_feature_protect)
    891         for api_call in self.cmdMembers:
    892             cmdname = api_call.name
    893             cmdinfo = cmd_info_dict[api_call.name]
    894             if cmdname in self.interface_functions:
    895                 continue
    896             if cmdname in self.no_autogen_list:
    897                 decls = self.makeCDecls(cmdinfo.elem)
    898                 self.appendSection('command', '')
    899                 self.appendSection('command', '// Declare only')
    900                 self.appendSection('command', decls[0])
    901                 self.intercepts += [ '    {"%s", (void *)%s},' % (cmdname,cmdname[2:]) ]
    902                 continue
    903             # Generate object handling code
    904             (api_decls, api_pre, api_post) = self.generate_wrapping_code(cmdinfo.elem)
    905             # If API doesn't contain any object handles, don't fool with it
    906             if not api_decls and not api_pre and not api_post:
    907                 continue
    908             feature_extra_protect = cmd_protect_dict[api_call.name]
    909             if (feature_extra_protect != None):
    910                 self.appendSection('command', '')
    911                 self.appendSection('command', '#ifdef '+ feature_extra_protect)
    912                 self.intercepts += [ '#ifdef %s' % feature_extra_protect ]
    913             # Add intercept to procmap
    914             self.intercepts += [ '    {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ]
    915             decls = self.makeCDecls(cmdinfo.elem)
    916             self.appendSection('command', '')
    917             self.appendSection('command', decls[0][:-1])
    918             self.appendSection('command', '{')
    919             self.appendSection('command', '    bool skip = false;')
    920             # Handle return values, if any
    921             resulttype = cmdinfo.elem.find('proto/type')
    922             if (resulttype != None and resulttype.text == 'void'):
    923               resulttype = None
    924             if (resulttype != None):
    925                 assignresult = resulttype.text + ' result = '
    926             else:
    927                 assignresult = ''
    928             # Pre-pend declarations and pre-api-call codegen
    929             if api_decls:
    930                 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n")))
    931             if api_pre:
    932                 self.appendSection('command', "\n".join(str(api_pre).rstrip().split("\n")))
    933             # Generate the API call itself
    934             # Gather the parameter items
    935             params = cmdinfo.elem.findall('param/name')
    936             # Pull out the text for each of the parameters, separate them by commas in a list
    937             paramstext = ', '.join([str(param.text) for param in params])
    938             # Use correct dispatch table
    939             disp_type = cmdinfo.elem.find('param/type').text
    940             disp_name = cmdinfo.elem.find('param/name').text
    941             dispatch_table = 'get_dispatch_table(ot_%s_table_map, %s)->' % (self.GetDispType(disp_type), disp_name)
    942             API = cmdinfo.elem.attrib.get('name').replace('vk', dispatch_table, 1)
    943             # Put all this together for the final down-chain call
    944             if assignresult != '':
    945                 if resulttype.text == 'VkResult':
    946                     self.appendSection('command', '    if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;')
    947                 elif resulttype.text == 'VkBool32':
    948                     self.appendSection('command', '    if (skip) return VK_FALSE;')
    949                 else:
    950                     raise Exception('Unknown result type ' + resulttype.text)
    951             else:
    952                 self.appendSection('command', '    if (skip) return;')
    953             self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
    954             # And add the post-API-call codegen
    955             self.appendSection('command', "\n".join(str(api_post).rstrip().split("\n")))
    956             # Handle the return result variable, if any
    957             if (resulttype != None):
    958                 self.appendSection('command', '    return result;')
    959             self.appendSection('command', '}')
    960             if (feature_extra_protect != None):
    961                 self.appendSection('command', '#endif // '+ feature_extra_protect)
    962                 self.intercepts += [ '#endif' ]
    963