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