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