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