Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/python3 -i
      2 #
      3 # Copyright (c) 2015-2019 The Khronos Group Inc.
      4 # Copyright (c) 2015-2019 Valve Corporation
      5 # Copyright (c) 2015-2019 LunarG, Inc.
      6 # Copyright (c) 2015-2019 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 # Author: Dave Houlton <daveh (at] lunarg.com>
     22 
     23 import os,re,sys,string,json
     24 import xml.etree.ElementTree as etree
     25 from generator import *
     26 from collections import namedtuple
     27 from common_codegen import *
     28 
     29 # This is a workaround to use a Python 2.7 and 3.x compatible syntax.
     30 from io import open
     31 
     32 # ObjectTrackerGeneratorOptions - subclass of GeneratorOptions.
     33 #
     34 # Adds options used by ObjectTrackerOutputGenerator objects during
     35 # object_tracker layer generation.
     36 #
     37 # Additional members
     38 #   prefixText - list of strings to prefix generated header with
     39 #     (usually a copyright statement + calling convention macros).
     40 #   protectFile - True if multiple inclusion protection should be
     41 #     generated (based on the filename) around the entire header.
     42 #   protectFeature - True if #ifndef..#endif protection should be
     43 #     generated around a feature interface in the header file.
     44 #   genFuncPointers - True if function pointer typedefs should be
     45 #     generated
     46 #   protectProto - If conditional protection should be generated
     47 #     around prototype declarations, set to either '#ifdef'
     48 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
     49 #     to require opt-out (#ifndef protectProtoStr). Otherwise
     50 #     set to None.
     51 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
     52 #     declarations, if protectProto is set
     53 #   apicall - string to use for the function declaration prefix,
     54 #     such as APICALL on Windows.
     55 #   apientry - string to use for the calling convention macro,
     56 #     in typedefs, such as APIENTRY.
     57 #   apientryp - string to use for the calling convention macro
     58 #     in function pointer typedefs, such as APIENTRYP.
     59 #   indentFuncProto - True if prototype declarations should put each
     60 #     parameter on a separate line
     61 #   indentFuncPointer - True if typedefed function pointers should put each
     62 #     parameter on a separate line
     63 #   alignFuncParam - if nonzero and parameters are being put on a
     64 #     separate line, align parameter names at the specified column
     65 class ObjectTrackerGeneratorOptions(GeneratorOptions):
     66     def __init__(self,
     67                  filename = None,
     68                  directory = '.',
     69                  apiname = None,
     70                  profile = None,
     71                  versions = '.*',
     72                  emitversions = '.*',
     73                  defaultExtensions = None,
     74                  addExtensions = None,
     75                  removeExtensions = None,
     76                  emitExtensions = None,
     77                  sortProcedure = regSortFeatures,
     78                  prefixText = "",
     79                  genFuncPointers = True,
     80                  protectFile = True,
     81                  protectFeature = True,
     82                  apicall = '',
     83                  apientry = '',
     84                  apientryp = '',
     85                  indentFuncProto = True,
     86                  indentFuncPointer = False,
     87                  alignFuncParam = 0,
     88                  expandEnumerants = True,
     89                  valid_usage_path = ''):
     90         GeneratorOptions.__init__(self, filename, directory, apiname, profile,
     91                                   versions, emitversions, defaultExtensions,
     92                                   addExtensions, removeExtensions, emitExtensions, sortProcedure)
     93         self.prefixText      = prefixText
     94         self.genFuncPointers = genFuncPointers
     95         self.protectFile     = protectFile
     96         self.protectFeature  = protectFeature
     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         self.expandEnumerants = expandEnumerants
    104         self.valid_usage_path = valid_usage_path
    105 
    106 
    107 # ObjectTrackerOutputGenerator - subclass of OutputGenerator.
    108 # Generates object_tracker layer object validation code
    109 #
    110 # ---- methods ----
    111 # ObjectTrackerOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
    112 # ---- methods overriding base class ----
    113 # beginFile(genOpts)
    114 # endFile()
    115 # beginFeature(interface, emit)
    116 # endFeature()
    117 # genCmd(cmdinfo)
    118 # genStruct()
    119 # genType()
    120 class ObjectTrackerOutputGenerator(OutputGenerator):
    121     """Generate ObjectTracker code based on XML element attributes"""
    122     # This is an ordered list of sections in the header file.
    123     ALL_SECTIONS = ['command']
    124     def __init__(self,
    125                  errFile = sys.stderr,
    126                  warnFile = sys.stderr,
    127                  diagFile = sys.stdout):
    128         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    129         self.INDENT_SPACES = 4
    130         self.prototypes = []
    131         self.instance_extensions = []
    132         self.device_extensions = []
    133         # Commands which are not autogenerated but still intercepted
    134         self.no_autogen_list = [
    135             'vkDestroyInstance',
    136             'vkCreateInstance',
    137             'vkEnumeratePhysicalDevices',
    138             'vkGetPhysicalDeviceQueueFamilyProperties',
    139             'vkGetPhysicalDeviceQueueFamilyProperties2',
    140             'vkGetPhysicalDeviceQueueFamilyProperties2KHR',
    141             'vkGetDeviceQueue',
    142             'vkGetDeviceQueue2',
    143             'vkCreateDescriptorSetLayout',
    144             'vkDestroyDescriptorPool',
    145             'vkDestroyCommandPool',
    146             'vkAllocateCommandBuffers',
    147             'vkAllocateDescriptorSets',
    148             'vkFreeDescriptorSets',
    149             'vkFreeCommandBuffers',
    150             'vkUpdateDescriptorSets',
    151             'vkBeginCommandBuffer',
    152             'vkGetDescriptorSetLayoutSupport',
    153             'vkGetDescriptorSetLayoutSupportKHR',
    154             'vkDestroySwapchainKHR',
    155             'vkGetSwapchainImagesKHR',
    156             'vkCmdPushDescriptorSetKHR',
    157             'vkDestroyDevice',
    158             'vkResetDescriptorPool',
    159             'vkGetPhysicalDeviceDisplayPropertiesKHR',
    160             'vkGetPhysicalDeviceDisplayProperties2KHR',
    161             'vkGetDisplayModePropertiesKHR',
    162             'vkGetDisplayModeProperties2KHR',
    163             ]
    164         # These VUIDS are not implicit, but are best handled in this layer. Codegen for vkDestroy calls will generate a key
    165         # which is translated here into a good VU.  Saves ~40 checks.
    166         self.manual_vuids = dict()
    167         self.manual_vuids = {
    168             "fence-compatalloc": "\"VUID-vkDestroyFence-fence-01121\"",
    169             "fence-nullalloc": "\"VUID-vkDestroyFence-fence-01122\"",
    170             "event-compatalloc": "\"VUID-vkDestroyEvent-event-01146\"",
    171             "event-nullalloc": "\"VUID-vkDestroyEvent-event-01147\"",
    172             "buffer-compatalloc": "\"VUID-vkDestroyBuffer-buffer-00923\"",
    173             "buffer-nullalloc": "\"VUID-vkDestroyBuffer-buffer-00924\"",
    174             "image-compatalloc": "\"VUID-vkDestroyImage-image-01001\"",
    175             "image-nullalloc": "\"VUID-vkDestroyImage-image-01002\"",
    176             "shaderModule-compatalloc": "\"VUID-vkDestroyShaderModule-shaderModule-01092\"",
    177             "shaderModule-nullalloc": "\"VUID-vkDestroyShaderModule-shaderModule-01093\"",
    178             "pipeline-compatalloc": "\"VUID-vkDestroyPipeline-pipeline-00766\"",
    179             "pipeline-nullalloc": "\"VUID-vkDestroyPipeline-pipeline-00767\"",
    180             "sampler-compatalloc": "\"VUID-vkDestroySampler-sampler-01083\"",
    181             "sampler-nullalloc": "\"VUID-vkDestroySampler-sampler-01084\"",
    182             "renderPass-compatalloc": "\"VUID-vkDestroyRenderPass-renderPass-00874\"",
    183             "renderPass-nullalloc": "\"VUID-vkDestroyRenderPass-renderPass-00875\"",
    184             "descriptorUpdateTemplate-compatalloc": "\"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00356\"",
    185             "descriptorUpdateTemplate-nullalloc": "\"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00357\"",
    186             "imageView-compatalloc": "\"VUID-vkDestroyImageView-imageView-01027\"",
    187             "imageView-nullalloc": "\"VUID-vkDestroyImageView-imageView-01028\"",
    188             "pipelineCache-compatalloc": "\"VUID-vkDestroyPipelineCache-pipelineCache-00771\"",
    189             "pipelineCache-nullalloc": "\"VUID-vkDestroyPipelineCache-pipelineCache-00772\"",
    190             "pipelineLayout-compatalloc": "\"VUID-vkDestroyPipelineLayout-pipelineLayout-00299\"",
    191             "pipelineLayout-nullalloc": "\"VUID-vkDestroyPipelineLayout-pipelineLayout-00300\"",
    192             "descriptorSetLayout-compatalloc": "\"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00284\"",
    193             "descriptorSetLayout-nullalloc": "\"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00285\"",
    194             "semaphore-compatalloc": "\"VUID-vkDestroySemaphore-semaphore-01138\"",
    195             "semaphore-nullalloc": "\"VUID-vkDestroySemaphore-semaphore-01139\"",
    196             "queryPool-compatalloc": "\"VUID-vkDestroyQueryPool-queryPool-00794\"",
    197             "queryPool-nullalloc": "\"VUID-vkDestroyQueryPool-queryPool-00795\"",
    198             "bufferView-compatalloc": "\"VUID-vkDestroyBufferView-bufferView-00937\"",
    199             "bufferView-nullalloc": "\"VUID-vkDestroyBufferView-bufferView-00938\"",
    200             "surface-compatalloc": "\"VUID-vkDestroySurfaceKHR-surface-01267\"",
    201             "surface-nullalloc": "\"VUID-vkDestroySurfaceKHR-surface-01268\"",
    202             "framebuffer-compatalloc": "\"VUID-vkDestroyFramebuffer-framebuffer-00893\"",
    203             "framebuffer-nullalloc": "\"VUID-vkDestroyFramebuffer-framebuffer-00894\"",
    204            }
    205 
    206         # Commands shadowed by interface functions and are not implemented
    207         self.interface_functions = [
    208             ]
    209         self.headerVersion = None
    210         # Internal state - accumulators for different inner block text
    211         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    212         self.cmd_list = []             # list of commands processed to maintain ordering
    213         self.cmd_info_dict = {}        # Per entry-point data for code generation and validation
    214         self.structMembers = []        # List of StructMemberData records for all Vulkan structs
    215         self.extension_structs = []    # List of all structs or sister-structs containing handles
    216                                        # A sister-struct may contain no handles but shares <validextensionstructs> with one that does
    217         self.structTypes = dict()      # Map of Vulkan struct typename to required VkStructureType
    218         self.struct_member_dict = dict()
    219         # Named tuples to store struct and command data
    220         self.StructType = namedtuple('StructType', ['name', 'value'])
    221         self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo', 'members', 'extra_protect', 'alias', 'iscreate', 'isdestroy', 'allocator'])
    222         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'isconst', 'isoptional', 'iscount', 'iscreate', 'len', 'extstructs', 'cdecl', 'islocal'])
    223         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
    224         self.object_types = []         # List of all handle types
    225         self.valid_vuids = set()       # Set of all valid VUIDs
    226         self.vuid_dict = dict()        # VUID dictionary (from JSON)
    227     #
    228     # Check if the parameter passed in is optional
    229     def paramIsOptional(self, param):
    230         # See if the handle is optional
    231         isoptional = False
    232         # Simple, if it's optional, return true
    233         optString = param.attrib.get('optional')
    234         if optString:
    235             if optString == 'true':
    236                 isoptional = True
    237             elif ',' in optString:
    238                 opts = []
    239                 for opt in optString.split(','):
    240                     val = opt.strip()
    241                     if val == 'true':
    242                         opts.append(True)
    243                     elif val == 'false':
    244                         opts.append(False)
    245                     else:
    246                         print('Unrecognized len attribute value',val)
    247                 isoptional = opts
    248         if not isoptional:
    249             # Matching logic in parameter validation and ValidityOutputGenerator.isHandleOptional
    250             optString = param.attrib.get('noautovalidity')
    251             if optString and optString == 'true':
    252                 isoptional = True;
    253         return isoptional
    254     #
    255     # Get VUID identifier from implicit VUID tag
    256     def GetVuid(self, parent, suffix):
    257         vuid_string = 'VUID-%s-%s' % (parent, suffix)
    258         vuid = "kVUIDUndefined"
    259         if '->' in vuid_string:
    260            return vuid
    261         if vuid_string in self.valid_vuids:
    262             vuid = "\"%s\"" % vuid_string
    263         else:
    264             alias =  self.cmd_info_dict[parent].alias if parent in self.cmd_info_dict else None
    265             if alias:
    266                 alias_string = 'VUID-%s-%s' % (alias, suffix)
    267                 if alias_string in self.valid_vuids:
    268                     vuid = "\"%s\"" % vuid_string
    269         return vuid
    270     #
    271     # Increases indent by 4 spaces and tracks it globally
    272     def incIndent(self, indent):
    273         inc = ' ' * self.INDENT_SPACES
    274         if indent:
    275             return indent + inc
    276         return inc
    277     #
    278     # Decreases indent by 4 spaces and tracks it globally
    279     def decIndent(self, indent):
    280         if indent and (len(indent) > self.INDENT_SPACES):
    281             return indent[:-self.INDENT_SPACES]
    282         return ''
    283     #
    284     # Override makeProtoName to drop the "vk" prefix
    285     def makeProtoName(self, name, tail):
    286         return self.genOpts.apientry + name[2:] + tail
    287     #
    288     # Check if the parameter passed in is a pointer to an array
    289     def paramIsArray(self, param):
    290         return param.attrib.get('len') is not None
    291 
    292     #
    293     # Generate the object tracker undestroyed object validation function
    294     def GenReportFunc(self):
    295         output_func = ''
    296         output_func += 'bool ObjectLifetimes::ReportUndestroyedObjects(VkDevice device, const std::string& error_code) {\n'
    297         output_func += '    bool skip = false;\n'
    298         output_func += '    skip |= DeviceReportUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer, error_code);\n'
    299         for handle in self.object_types:
    300             if self.isHandleTypeNonDispatchable(handle):
    301                 output_func += '    skip |= DeviceReportUndestroyedObjects(device, %s, error_code);\n' % (self.GetVulkanObjType(handle))
    302         output_func += '    return skip;\n'
    303         output_func += '}\n'
    304         return output_func
    305 
    306     #
    307     # Generate the object tracker undestroyed object destruction function
    308     def GenDestroyFunc(self):
    309         output_func = ''
    310         output_func += 'void ObjectLifetimes::DestroyUndestroyedObjects(VkDevice device) {\n'
    311         output_func += '    DeviceDestroyUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer);\n'
    312         for handle in self.object_types:
    313             if self.isHandleTypeNonDispatchable(handle):
    314                 output_func += '    DeviceDestroyUndestroyedObjects(device, %s);\n' % (self.GetVulkanObjType(handle))
    315         output_func += '}\n'
    316         return output_func
    317 
    318     #
    319     # Walk the JSON-derived dict and find all "vuid" key values
    320     def ExtractVUIDs(self, d):
    321         if hasattr(d, 'items'):
    322             for k, v in d.items():
    323                 if k == "vuid":
    324                     yield v
    325                 elif isinstance(v, dict):
    326                     for s in self.ExtractVUIDs(v):
    327                         yield s
    328                 elif isinstance (v, list):
    329                     for l in v:
    330                         for s in self.ExtractVUIDs(l):
    331                             yield s
    332     #
    333     # Separate content for validation source and header files
    334     def otwrite(self, dest, formatstring):
    335         if 'object_tracker.h' in self.genOpts.filename and (dest == 'hdr' or dest == 'both'):
    336             write(formatstring, file=self.outFile)
    337         elif 'object_tracker.cpp' in self.genOpts.filename and (dest == 'cpp' or dest == 'both'):
    338             write(formatstring, file=self.outFile)
    339 
    340     #
    341     # Called at beginning of processing as file is opened
    342     def beginFile(self, genOpts):
    343         OutputGenerator.beginFile(self, genOpts)
    344 
    345         header_file = (genOpts.filename == 'object_tracker.h')
    346         source_file = (genOpts.filename == 'object_tracker.cpp')
    347 
    348         if not header_file and not source_file:
    349             print("Error: Output Filenames have changed, update generator source.\n")
    350             sys.exit(1)
    351 
    352         self.valid_usage_path = genOpts.valid_usage_path
    353         vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
    354         if os.path.isfile(vu_json_filename):
    355             json_file = open(vu_json_filename, 'r')
    356             self.vuid_dict = json.load(json_file)
    357             json_file.close()
    358         if len(self.vuid_dict) == 0:
    359             print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
    360             sys.exit(1)
    361 
    362         # Build a set of all vuid text strings found in validusage.json
    363         for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
    364             self.valid_vuids.add(json_vuid_string)
    365 
    366         # File Comment
    367         file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n'
    368         file_comment += '// See object_tracker_generator.py for modifications\n'
    369         self.otwrite('both', file_comment)
    370         # Copyright Statement
    371         copyright = ''
    372         copyright += '\n'
    373         copyright += '/***************************************************************************\n'
    374         copyright += ' *\n'
    375         copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n'
    376         copyright += ' * Copyright (c) 2015-2019 Valve Corporation\n'
    377         copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n'
    378         copyright += ' * Copyright (c) 2015-2019 Google Inc.\n'
    379         copyright += ' *\n'
    380         copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
    381         copyright += ' * you may not use this file except in compliance with the License.\n'
    382         copyright += ' * You may obtain a copy of the License at\n'
    383         copyright += ' *\n'
    384         copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
    385         copyright += ' *\n'
    386         copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
    387         copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
    388         copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
    389         copyright += ' * See the License for the specific language governing permissions and\n'
    390         copyright += ' * limitations under the License.\n'
    391         copyright += ' *\n'
    392         copyright += ' * Author: Mark Lobodzinski <mark (at] lunarg.com>\n'
    393         copyright += ' * Author: Dave Houlton <daveh (at] lunarg.com>\n'
    394         copyright += ' *\n'
    395         copyright += ' ****************************************************************************/\n'
    396         self.otwrite('both', copyright)
    397         self.newline()
    398         self.otwrite('cpp', '#include "chassis.h"')
    399         self.otwrite('cpp', '#include "object_lifetime_validation.h"')
    400 
    401     #
    402     # Now that the data is all collected and complete, generate and output the object validation routines
    403     def endFile(self):
    404         self.struct_member_dict = dict(self.structMembers)
    405         # Generate the list of APIs that might need to handle wrapped extension structs
    406         # self.GenerateCommandWrapExtensionList()
    407         self.WrapCommands()
    408         # Build undestroyed objects reporting function
    409         report_func = self.GenReportFunc()
    410         self.newline()
    411         # Build undestroyed objects destruction function
    412         destroy_func = self.GenDestroyFunc()
    413         self.otwrite('cpp', '\n')
    414         self.otwrite('cpp', '// ObjectTracker undestroyed objects validation function')
    415         self.otwrite('cpp', '%s' % report_func)
    416         self.otwrite('cpp', '%s' % destroy_func)
    417         # Actually write the interface to the output file.
    418         if (self.emit):
    419             self.newline()
    420             if self.featureExtraProtect is not None:
    421                 prot = '#ifdef %s' % self.featureExtraProtect
    422                 self.otwrite('both', '%s' % prot)
    423             # Write the object_tracker code to the  file
    424             if self.sections['command']:
    425                 source = ('\n'.join(self.sections['command']))
    426                 self.otwrite('both', '%s' % source)
    427             if (self.featureExtraProtect is not None):
    428                 prot = '\n#endif // %s', self.featureExtraProtect
    429                 self.otwrite('both', prot)
    430             else:
    431                 self.otwrite('both', '\n')
    432 
    433 
    434         self.otwrite('hdr', 'void PostCallRecordDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator);')
    435         self.otwrite('hdr', 'void PreCallRecordResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);')
    436         self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties);')
    437         self.otwrite('hdr', 'void PreCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers);')
    438         self.otwrite('hdr', 'void PreCallRecordFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets);')
    439         self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR *pQueueFamilyProperties);')
    440         self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR *pQueueFamilyProperties);')
    441         self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties, VkResult result);')
    442         self.otwrite('hdr', 'void PostCallRecordGetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties, VkResult result);')
    443         self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceDisplayProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayProperties2KHR *pProperties, VkResult result);')
    444         self.otwrite('hdr', 'void PostCallRecordGetDisplayModeProperties2KHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModeProperties2KHR *pProperties, VkResult result);')
    445         OutputGenerator.endFile(self)
    446     #
    447     # Processing point at beginning of each extension definition
    448     def beginFeature(self, interface, emit):
    449         # Start processing in superclass
    450         OutputGenerator.beginFeature(self, interface, emit)
    451         self.headerVersion = None
    452         self.featureExtraProtect = GetFeatureProtect(interface)
    453 
    454         if self.featureName != 'VK_VERSION_1_0' and self.featureName != 'VK_VERSION_1_1':
    455             white_list_entry = []
    456             if (self.featureExtraProtect is not None):
    457                 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ]
    458             white_list_entry += [ '"%s"' % self.featureName ]
    459             if (self.featureExtraProtect is not None):
    460                 white_list_entry += [ '#endif' ]
    461             featureType = interface.get('type')
    462             if featureType == 'instance':
    463                 self.instance_extensions += white_list_entry
    464             elif featureType == 'device':
    465                 self.device_extensions += white_list_entry
    466     #
    467     # Processing point at end of each extension definition
    468     def endFeature(self):
    469         # Finish processing in superclass
    470         OutputGenerator.endFeature(self)
    471     #
    472     # Process enums, structs, etc.
    473     def genType(self, typeinfo, name, alias):
    474         OutputGenerator.genType(self, typeinfo, name, alias)
    475         typeElem = typeinfo.elem
    476         # If the type is a struct type, traverse the imbedded <member> tags generating a structure.
    477         # Otherwise, emit the tag text.
    478         category = typeElem.get('category')
    479         if (category == 'struct' or category == 'union'):
    480             self.genStruct(typeinfo, name, alias)
    481         if category == 'handle':
    482             self.object_types.append(name)
    483     #
    484     # Append a definition to the specified section
    485     def appendSection(self, section, text):
    486         # self.sections[section].append('SECTION: ' + section + '\n')
    487         self.sections[section].append(text)
    488     #
    489     # Check if the parameter passed in is a pointer
    490     def paramIsPointer(self, param):
    491         ispointer = False
    492         for elem in param:
    493             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
    494                 ispointer = True
    495         return ispointer
    496     #
    497     # Get the category of a type
    498     def getTypeCategory(self, typename):
    499         types = self.registry.tree.findall("types/type")
    500         for elem in types:
    501             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
    502                 return elem.attrib.get('category')
    503     #
    504     # Check if a parent object is dispatchable or not
    505     def isHandleTypeObject(self, handletype):
    506         handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
    507         if handle is not None:
    508             return True
    509         else:
    510             return False
    511     #
    512     # Check if a parent object is dispatchable or not
    513     def isHandleTypeNonDispatchable(self, handletype):
    514         handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']")
    515         if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
    516             return True
    517         else:
    518             return False
    519     #
    520     # Retrieve the type and name for a parameter
    521     def getTypeNameTuple(self, param):
    522         type = ''
    523         name = ''
    524         for elem in param:
    525             if elem.tag == 'type':
    526                 type = noneStr(elem.text)
    527             elif elem.tag == 'name':
    528                 name = noneStr(elem.text)
    529         return (type, name)
    530     #
    531     # Retrieve the value of the len tag
    532     def getLen(self, param):
    533         result = None
    534         len = param.attrib.get('len')
    535         if len and len != 'null-terminated':
    536             # For string arrays, 'len' can look like 'count,null-terminated', indicating that we
    537             # have a null terminated array of strings.  We strip the null-terminated from the
    538             # 'len' field and only return the parameter specifying the string count
    539             if 'null-terminated' in len:
    540                 result = len.split(',')[0]
    541             else:
    542                 result = len
    543             # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
    544             result = str(result).replace('::', '->')
    545         return result
    546     #
    547     # Generate a VkStructureType based on a structure typename
    548     def genVkStructureType(self, typename):
    549         # Add underscore between lowercase then uppercase
    550         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
    551         # Change to uppercase
    552         value = value.upper()
    553         # Add STRUCTURE_TYPE_
    554         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
    555     #
    556     # Struct parameter check generation.
    557     # This is a special case of the <type> tag where the contents are interpreted as a set of
    558     # <member> tags instead of freeform C type declarations. The <member> tags are just like
    559     # <param> tags - they are a declaration of a struct or union member. Only simple member
    560     # declarations are supported (no nested structs etc.)
    561     def genStruct(self, typeinfo, typeName, alias):
    562         OutputGenerator.genStruct(self, typeinfo, typeName, alias)
    563         members = typeinfo.elem.findall('.//member')
    564         # Iterate over members once to get length parameters for arrays
    565         lens = set()
    566         for member in members:
    567             len = self.getLen(member)
    568             if len:
    569                 lens.add(len)
    570         # Generate member info
    571         membersInfo = []
    572         for member in members:
    573             # Get the member's type and name
    574             info = self.getTypeNameTuple(member)
    575             type = info[0]
    576             name = info[1]
    577             cdecl = self.makeCParamDecl(member, 0)
    578             # Process VkStructureType
    579             if type == 'VkStructureType':
    580                 # Extract the required struct type value from the comments
    581                 # embedded in the original text defining the 'typeinfo' element
    582                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
    583                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
    584                 if result:
    585                     value = result.group(0)
    586                 else:
    587                     value = self.genVkStructureType(typeName)
    588                 # Store the required type value
    589                 self.structTypes[typeName] = self.StructType(name=name, value=value)
    590             # Store pointer/array/string info
    591             extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
    592             membersInfo.append(self.CommandParam(type=type,
    593                                                  name=name,
    594                                                  isconst=True if 'const' in cdecl else False,
    595                                                  isoptional=self.paramIsOptional(member),
    596                                                  iscount=True if name in lens else False,
    597                                                  len=self.getLen(member),
    598                                                  extstructs=extstructs,
    599                                                  cdecl=cdecl,
    600                                                  islocal=False,
    601                                                  iscreate=False))
    602         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
    603     #
    604     # Insert a lock_guard line
    605     def lock_guard(self, indent):
    606         return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
    607     #
    608     # Determine if a struct has an object as a member or an embedded member
    609     def struct_contains_object(self, struct_item):
    610         struct_member_dict = dict(self.structMembers)
    611         struct_members = struct_member_dict[struct_item]
    612 
    613         for member in struct_members:
    614             if self.isHandleTypeObject(member.type):
    615                 return True
    616             # recurse for member structs, guard against infinite recursion
    617             elif member.type in struct_member_dict and member.type != struct_item:
    618                 if self.struct_contains_object(member.type):
    619                     return True
    620         return False
    621     #
    622     # Return list of struct members which contain, or whose sub-structures contain an obj in a given list of parameters or members
    623     def getParmeterStructsWithObjects(self, item_list):
    624         struct_list = set()
    625         for item in item_list:
    626             paramtype = item.find('type')
    627             typecategory = self.getTypeCategory(paramtype.text)
    628             if typecategory == 'struct':
    629                 if self.struct_contains_object(paramtype.text) == True:
    630                     struct_list.add(item)
    631         return struct_list
    632     #
    633     # Return list of objects from a given list of parameters or members
    634     def getObjectsInParameterList(self, item_list, create_func):
    635         object_list = set()
    636         if create_func == True:
    637             member_list = item_list[0:-1]
    638         else:
    639             member_list = item_list
    640         for item in member_list:
    641             if self.isHandleTypeObject(paramtype.text):
    642                 object_list.add(item)
    643         return object_list
    644     #
    645     # Construct list of extension structs containing handles, or extension structs that share a <validextensionstructs>
    646     # tag WITH an extension struct containing handles.
    647     def GenerateCommandWrapExtensionList(self):
    648         for struct in self.structMembers:
    649             if (len(struct.members) > 1) and struct.members[1].extstructs is not None:
    650                 found = False;
    651                 for item in struct.members[1].extstructs.split(','):
    652                     if item != '' and self.struct_contains_object(item) == True:
    653                         found = True
    654                 if found == True:
    655                     for item in struct.members[1].extstructs.split(','):
    656                         if item != '' and item not in self.extension_structs:
    657                             self.extension_structs.append(item)
    658     #
    659     # Returns True if a struct may have a pNext chain containing an object
    660     def StructWithExtensions(self, struct_type):
    661         if struct_type in self.struct_member_dict:
    662             param_info = self.struct_member_dict[struct_type]
    663             if (len(param_info) > 1) and param_info[1].extstructs is not None:
    664                 for item in param_info[1].extstructs.split(','):
    665                     if item in self.extension_structs:
    666                         return True
    667         return False
    668     #
    669     # Generate VulkanObjectType from object type
    670     def GetVulkanObjType(self, type):
    671         return 'kVulkanObjectType%s' % type[2:]
    672     #
    673     # Return correct dispatch table type -- instance or device
    674     def GetDispType(self, type):
    675         return 'instance' if type in ['VkInstance', 'VkPhysicalDevice'] else 'device'
    676     #
    677     # Generate source for creating a Vulkan object
    678     def generate_create_object_code(self, indent, proto, params, cmd_info, allocator):
    679         create_obj_code = ''
    680         handle_type = params[-1].find('type')
    681         is_create_pipelines = False
    682 
    683         if self.isHandleTypeObject(handle_type.text):
    684             # Check for special case where multiple handles are returned
    685             object_array = False
    686             if cmd_info[-1].len is not None:
    687                 object_array = True;
    688             handle_name = params[-1].find('name')
    689             object_dest = '*%s' % handle_name.text
    690             if object_array == True:
    691                 if 'CreateGraphicsPipelines' in proto.text or 'CreateComputePipelines' in proto.text or 'CreateRayTracingPipelines' in proto.text:
    692                     is_create_pipelines = True
    693                     create_obj_code += '%sif (VK_ERROR_VALIDATION_FAILED_EXT == result) return;\n' % indent
    694                 countispointer = ''
    695                 if 'uint32_t*' in cmd_info[-2].cdecl:
    696                     countispointer = '*'
    697                 create_obj_code += '%sfor (uint32_t index = 0; index < %s%s; index++) {\n' % (indent, countispointer, cmd_info[-1].len)
    698                 indent = self.incIndent(indent)
    699                 object_dest = '%s[index]' % cmd_info[-1].name
    700 
    701             dispobj = params[0].find('type').text
    702             if is_create_pipelines:
    703                 create_obj_code += '%sif (!pPipelines[index]) continue;\n' % indent
    704             create_obj_code += '%sCreateObject(%s, %s, %s, %s);\n' % (indent, params[0].find('name').text, object_dest, self.GetVulkanObjType(cmd_info[-1].type), allocator)
    705             if object_array == True:
    706                 indent = self.decIndent(indent)
    707                 create_obj_code += '%s}\n' % indent
    708             indent = self.decIndent(indent)
    709         return create_obj_code
    710     #
    711     # Generate source for destroying a non-dispatchable object
    712     def generate_destroy_object_code(self, indent, proto, cmd_info):
    713         validate_code = ''
    714         record_code = ''
    715         object_array = False
    716         if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]:
    717             # Check for special case where multiple handles are returned
    718             if cmd_info[-1].len is not None:
    719                 object_array = True;
    720                 param = -1
    721             else:
    722                 param = -2
    723             compatalloc_vuid_string = '%s-compatalloc' % cmd_info[param].name
    724             nullalloc_vuid_string = '%s-nullalloc' % cmd_info[param].name
    725             compatalloc_vuid = self.manual_vuids.get(compatalloc_vuid_string, "kVUIDUndefined")
    726             nullalloc_vuid = self.manual_vuids.get(nullalloc_vuid_string, "kVUIDUndefined")
    727             if self.isHandleTypeObject(cmd_info[param].type) == True:
    728                 if object_array == True:
    729                     # This API is freeing an array of handles -- add loop control
    730                     validate_code += 'HEY, NEED TO DESTROY AN ARRAY\n'
    731                 else:
    732                     dispobj = cmd_info[0].type
    733                     # Call Destroy a single time
    734                     validate_code += '%sskip |= ValidateDestroyObject(%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)
    735                     record_code += '%sRecordDestroyObject(%s, %s, %s);\n' % (indent, cmd_info[0].name, cmd_info[param].name, self.GetVulkanObjType(cmd_info[param].type))
    736         return object_array, validate_code, record_code
    737     #
    738     # Output validation for a single object (obj_count is NULL) or a counted list of objects
    739     def outputObjects(self, obj_type, obj_name, obj_count, prefix, index, indent, disp_name, parent_name, null_allowed, top_level):
    740         pre_call_code = ''
    741         param_suffix = '%s-parameter' % (obj_name)
    742         parent_suffix = '%s-parent' % (obj_name)
    743         param_vuid = self.GetVuid(parent_name, param_suffix)
    744         parent_vuid = self.GetVuid(parent_name, parent_suffix)
    745 
    746         # If no parent VUID for this member, look for a commonparent VUID
    747         if parent_vuid == 'kVUIDUndefined':
    748             parent_vuid = self.GetVuid(parent_name, 'commonparent')
    749         if obj_count is not None:
    750             pre_call_code += '%sfor (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, obj_count, index)
    751             indent = self.incIndent(indent)
    752             pre_call_code += '%sskip |= 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)
    753             indent = self.decIndent(indent)
    754             pre_call_code += '%s}\n' % indent
    755         else:
    756             pre_call_code += '%sskip |= 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)
    757         return pre_call_code
    758     #
    759     # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct
    760     def validate_objects(self, members, indent, prefix, array_index, disp_name, parent_name, first_level_param):
    761         pre_code = ''
    762         index = 'index%s' % str(array_index)
    763         array_index += 1
    764         # Process any objects in this structure and recurse for any sub-structs in this struct
    765         for member in members:
    766             # Handle objects
    767             if member.iscreate and first_level_param and member == members[-1]:
    768                 continue
    769             if self.isHandleTypeObject(member.type) == True:
    770                 count_name = member.len
    771                 if (count_name is not None):
    772                     count_name = '%s%s' % (prefix, member.len)
    773                 null_allowed = member.isoptional
    774                 tmp_pre = self.outputObjects(member.type, member.name, count_name, prefix, index, indent, disp_name, parent_name, str(null_allowed).lower(), first_level_param)
    775                 pre_code += tmp_pre
    776             # Handle Structs that contain objects at some level
    777             elif member.type in self.struct_member_dict:
    778                 # Structs at first level will have an object
    779                 if self.struct_contains_object(member.type) == True:
    780                     struct_info = self.struct_member_dict[member.type]
    781                     # TODO (jbolz): Can this use paramIsPointer?
    782                     ispointer = '*' in member.cdecl;
    783                     # Struct Array
    784                     if member.len is not None:
    785                         # Update struct prefix
    786                         new_prefix = '%s%s' % (prefix, member.name)
    787                         pre_code += '%sif (%s%s) {\n' % (indent, prefix, member.name)
    788                         indent = self.incIndent(indent)
    789                         pre_code += '%sfor (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index)
    790                         indent = self.incIndent(indent)
    791                         local_prefix = '%s[%s].' % (new_prefix, index)
    792                         # Process sub-structs in this struct
    793                         tmp_pre = self.validate_objects(struct_info, indent, local_prefix, array_index, disp_name, member.type, False)
    794                         pre_code += tmp_pre
    795                         indent = self.decIndent(indent)
    796                         pre_code += '%s}\n' % indent
    797                         indent = self.decIndent(indent)
    798                         pre_code += '%s}\n' % indent
    799                     # Single Struct
    800                     elif ispointer:
    801                         # Update struct prefix
    802                         new_prefix = '%s%s->' % (prefix, member.name)
    803                         # Declare safe_VarType for struct
    804                         pre_code += '%sif (%s%s) {\n' % (indent, prefix, member.name)
    805                         indent = self.incIndent(indent)
    806                         # Process sub-structs in this struct
    807                         tmp_pre = self.validate_objects(struct_info, indent, new_prefix, array_index, disp_name, member.type, False)
    808                         pre_code += tmp_pre
    809                         indent = self.decIndent(indent)
    810                         pre_code += '%s}\n' % indent
    811         return pre_code
    812     #
    813     # For a particular API, generate the object handling code
    814     def generate_wrapping_code(self, cmd):
    815         indent = '    '
    816         pre_call_validate = ''
    817         pre_call_record = ''
    818         post_call_record = ''
    819 
    820         destroy_array = False
    821         validate_destroy_code = ''
    822         record_destroy_code = ''
    823 
    824         proto = cmd.find('proto/name')
    825         params = cmd.findall('param')
    826         if proto.text is not None:
    827             cmddata = self.cmd_info_dict[proto.text]
    828             cmd_info = cmddata.members
    829             disp_name = cmd_info[0].name
    830             # Handle object create operations if last parameter is created by this call
    831             if cmddata.iscreate:
    832                 post_call_record += self.generate_create_object_code(indent, proto, params, cmd_info, cmddata.allocator)
    833             # Handle object destroy operations
    834             if cmddata.isdestroy:
    835                 (destroy_array, validate_destroy_code, record_destroy_code) = self.generate_destroy_object_code(indent, proto, cmd_info)
    836 
    837             pre_call_record += record_destroy_code
    838             pre_call_validate += self.validate_objects(cmd_info, indent, '', 0, disp_name, proto.text, True)
    839             pre_call_validate += validate_destroy_code
    840 
    841         return pre_call_validate, pre_call_record, post_call_record
    842     #
    843     # Capture command parameter info needed to create, destroy, and validate objects
    844     def genCmd(self, cmdinfo, cmdname, alias):
    845 
    846         # Add struct-member type information to command parameter information
    847         OutputGenerator.genCmd(self, cmdinfo, cmdname, alias)
    848         members = cmdinfo.elem.findall('.//param')
    849         # Iterate over members once to get length parameters for arrays
    850         lens = set()
    851         for member in members:
    852             length = self.getLen(member)
    853             if length:
    854                 lens.add(length)
    855         struct_member_dict = dict(self.structMembers)
    856 
    857         # Set command invariant information needed at a per member level in validate...
    858         is_create_command = any(filter(lambda pat: pat in cmdname, ('Create', 'Allocate', 'Enumerate', 'RegisterDeviceEvent', 'RegisterDisplayEvent')))
    859         last_member_is_pointer = len(members) and self.paramIsPointer(members[-1])
    860         iscreate = is_create_command or ('vkGet' in cmdname and last_member_is_pointer)
    861         isdestroy = any([destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']])
    862 
    863         # Generate member info
    864         membersInfo = []
    865         constains_extension_structs = False
    866         allocator = 'nullptr'
    867         for member in members:
    868             # Get type and name of member
    869             info = self.getTypeNameTuple(member)
    870             type = info[0]
    871             name = info[1]
    872             cdecl = self.makeCParamDecl(member, 0)
    873             # Check for parameter name in lens set
    874             iscount = True if name in lens else False
    875             length = self.getLen(member)
    876             isconst = True if 'const' in cdecl else False
    877             # Mark param as local if it is an array of objects
    878             islocal = False;
    879             if self.isHandleTypeObject(type) == True:
    880                 if (length is not None) and (isconst == True):
    881                     islocal = True
    882             # Or if it's a struct that contains an object
    883             elif type in struct_member_dict:
    884                 if self.struct_contains_object(type) == True:
    885                     islocal = True
    886             if type == 'VkAllocationCallbacks':
    887                 allocator = name
    888             extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None
    889             membersInfo.append(self.CommandParam(type=type,
    890                                                  name=name,
    891                                                  isconst=isconst,
    892                                                  isoptional=self.paramIsOptional(member),
    893                                                  iscount=iscount,
    894                                                  len=length,
    895                                                  extstructs=extstructs,
    896                                                  cdecl=cdecl,
    897                                                  islocal=islocal,
    898                                                  iscreate=iscreate))
    899 
    900         self.cmd_list.append(cmdname)
    901         self.cmd_info_dict[cmdname] =self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo, members=membersInfo, iscreate=iscreate, isdestroy=isdestroy, allocator=allocator, extra_protect=self.featureExtraProtect, alias=alias)
    902     #
    903     # Create code Create, Destroy, and validate Vulkan objects
    904     def WrapCommands(self):
    905         for cmdname in self.cmd_list:
    906             cmddata = self.cmd_info_dict[cmdname]
    907             cmdinfo = cmddata.cmdinfo
    908             if cmdname in self.interface_functions:
    909                 continue
    910             manual = False
    911             if cmdname in self.no_autogen_list:
    912                 manual = True
    913 
    914             # Generate object handling code
    915             (pre_call_validate, pre_call_record, post_call_record) = self.generate_wrapping_code(cmdinfo.elem)
    916 
    917             feature_extra_protect = cmddata.extra_protect
    918             if (feature_extra_protect is not None):
    919                 self.appendSection('command', '')
    920                 self.appendSection('command', '#ifdef '+ feature_extra_protect)
    921                 self.prototypes += [ '#ifdef %s' % feature_extra_protect ]
    922 
    923             # Add intercept to procmap
    924             self.prototypes += [ '    {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ]
    925 
    926             decls = self.makeCDecls(cmdinfo.elem)
    927 
    928             # Gather the parameter items
    929             params = cmdinfo.elem.findall('param/name')
    930             # Pull out the text for each of the parameters, separate them by commas in a list
    931             paramstext = ', '.join([str(param.text) for param in params])
    932             # Generate the API call template
    933             fcn_call = cmdinfo.elem.attrib.get('name').replace('vk', 'TOKEN', 1) + '(' + paramstext + ');'
    934 
    935             func_decl_template = decls[0][:-1].split('VKAPI_CALL ')
    936             func_decl_template = func_decl_template[1]
    937 
    938             result_type = cmdinfo.elem.find('proto/type')
    939 
    940             if 'object_tracker.h' in self.genOpts.filename:
    941                 # Output PreCallValidateAPI prototype if necessary
    942                 if pre_call_validate:
    943                     pre_cv_func_decl = 'bool PreCallValidate' + func_decl_template + ';'
    944                     self.appendSection('command', pre_cv_func_decl)
    945 
    946                 # Output PreCallRecordAPI prototype if necessary
    947                 if pre_call_record:
    948                     pre_cr_func_decl = 'void PreCallRecord' + func_decl_template + ';'
    949                     self.appendSection('command', pre_cr_func_decl)
    950 
    951                 # Output PosCallRecordAPI prototype if necessary
    952                 if post_call_record:
    953                     post_cr_func_decl = 'void PostCallRecord' + func_decl_template + ';'
    954                     if result_type.text == 'VkResult':
    955                         post_cr_func_decl = post_cr_func_decl.replace(')', ',\n    VkResult                                    result)')
    956                     self.appendSection('command', post_cr_func_decl)
    957 
    958             if 'object_tracker.cpp' in self.genOpts.filename:
    959                 # Output PreCallValidateAPI function if necessary
    960                 if pre_call_validate and not manual:
    961                     pre_cv_func_decl = 'bool ObjectLifetimes::PreCallValidate' + func_decl_template + ' {'
    962                     self.appendSection('command', '')
    963                     self.appendSection('command', pre_cv_func_decl)
    964                     self.appendSection('command', '    bool skip = false;')
    965                     self.appendSection('command', pre_call_validate)
    966                     self.appendSection('command', '    return skip;')
    967                     self.appendSection('command', '}')
    968 
    969                 # Output PreCallRecordAPI function if necessary
    970                 if pre_call_record and not manual:
    971                     pre_cr_func_decl = 'void ObjectLifetimes::PreCallRecord' + func_decl_template + ' {'
    972                     self.appendSection('command', '')
    973                     self.appendSection('command', pre_cr_func_decl)
    974                     self.appendSection('command', pre_call_record)
    975                     self.appendSection('command', '}')
    976 
    977                 # Output PosCallRecordAPI function if necessary
    978                 if post_call_record and not manual:
    979                     post_cr_func_decl = 'void ObjectLifetimes::PostCallRecord' + func_decl_template + ' {'
    980                     self.appendSection('command', '')
    981 
    982                     if result_type.text == 'VkResult':
    983                         post_cr_func_decl = post_cr_func_decl.replace(')', ',\n    VkResult                                    result)')
    984                         # The two createpipelines APIs may create on failure -- skip the success result check
    985                         if 'CreateGraphicsPipelines' not in cmdname and 'CreateComputePipelines' not in cmdname and 'CreateRayTracingPipelines' not in cmdname:
    986                             post_cr_func_decl = post_cr_func_decl.replace('{', '{\n    if (result != VK_SUCCESS) return;')
    987                     self.appendSection('command', post_cr_func_decl)
    988 
    989 
    990                     self.appendSection('command', post_call_record)
    991                     self.appendSection('command', '}')
    992 
    993             if (feature_extra_protect is not None):
    994                 self.appendSection('command', '#endif // '+ feature_extra_protect)
    995                 self.prototypes += [ '#endif' ]
    996