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