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