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.
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
12 # http://www.apache.org/licenses/LICENSE-2.0
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.
20 # Author: Mark Lobodzinski <mark@lunarg.com>
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 *
29 # This is a workaround to use a Python 2.7 and 3.x compatible syntax.
32 # ObjectTrackerGeneratorOptions - subclass of GeneratorOptions.
34 # Adds options used by ObjectTrackerOutputGenerator objects during
35 # object_tracker layer generation.
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
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
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):
73 defaultExtensions = None,
75 removeExtensions = None,
76 emitExtensions = None,
77 sortProcedure = regSortFeatures,
79 genFuncPointers = True,
81 protectFeature = True,
85 indentFuncProto = True,
86 indentFuncPointer = False,
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
105 # ObjectTrackerOutputGenerator - subclass of OutputGenerator.
106 # Generates object_tracker layer object validation code
109 # ObjectTrackerOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state.
110 # ---- methods overriding base class ----
113 # beginFeature(interface, emit)
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']
123 errFile = sys.stderr,
124 warnFile = sys.stderr,
125 diagFile = sys.stdout):
126 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
127 self.INDENT_SPACES = 4
129 self.instance_extensions = []
130 self.device_extensions = []
131 # Commands which are not autogenerated but still intercepted
132 self.no_autogen_list = [
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',
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',
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',
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",
230 # Commands shadowed by interface functions and are not implemented
231 self.interface_functions = [
233 self.headerVersion = None
234 # Internal state - accumulators for different inner block text
235 self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
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',
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")
268 if self.vuid_file == None:
269 print("Error: Could not find vk_validation_error_messages.h")
271 os.chdir(previous_dir)
273 # Check if the parameter passed in is optional
274 def paramIsOptional(self, param):
275 # See if the handle is optional
277 # Simple, if it's optional, return true
278 optString = param.attrib.get('optional')
280 if optString == 'true':
282 elif ',' in optString:
284 for opt in optString.split(','):
291 print('Unrecognized len attribute value',val)
294 # Matching logic in parameter validation and ValidityOutputGenerator.isHandleOptional
295 optString = param.attrib.get('noautovalidity')
296 if optString and optString == 'true':
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))
305 hex_num = hex(dec_num)
306 return hex_num[2:].zfill(8)
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
316 vuid = "VALIDATION_ERROR_UNDEFINED"
319 # Increases indent by 4 spaces and tracks it globally
320 def incIndent(self, indent):
321 inc = ' ' * self.INDENT_SPACES
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]
332 # Override makeProtoName to drop the "vk" prefix
333 def makeProtoName(self, name, tail):
334 return self.genOpts.apientry + name[2:] + tail
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
341 # Generate the object tracker undestroyed object validation function
342 def GenReportFunc(self):
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))
353 # Generate the object tracker undestroyed object destruction function
354 def GenDestroyFunc(self):
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))
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)
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
385 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'
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'
396 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\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'
404 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n'
406 copyright += ' ****************************************************************************/\n'
407 write(copyright, file=self.outFile)
410 write('#include "object_tracker.h"', file = self.outFile)
412 write('namespace object_tracker {', file = self.outFile)
414 # Now that the data is all collected and complete, generate and output the object validation routines
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()
420 # Build undestroyed objects reporting function
421 report_func = self.GenReportFunc()
423 # Build undestroyed objects destruction function
424 destroy_func = self.GenDestroyFunc()
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.
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)
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)
448 write('} // namespace object_tracker', file=self.outFile)
449 # Finish processing in superclass
450 OutputGenerator.endFile(self)
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)
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
472 # Processing point at end of each extension definition
473 def endFeature(self):
474 # Finish processing in superclass
475 OutputGenerator.endFeature(self)
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)
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)
494 # Check if the parameter passed in is a pointer
495 def paramIsPointer(self, param):
498 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
502 # Get the category of a type
503 def getTypeCategory(self, typename):
504 types = self.registry.tree.findall("types/type")
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')
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:
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':
525 # Retrieve the type and name for a parameter
526 def getTypeNameTuple(self, param):
530 if elem.tag == 'type':
531 type = noneStr(elem.text)
532 elif elem.tag == 'name':
533 name = noneStr(elem.text)
536 # Retrieve the value of the len tag
537 def getLen(self, param):
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]
548 # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol
549 result = str(result).replace('::', '->')
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)
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
571 for member in members:
572 len = self.getLen(member)
575 # Generate member info
577 for member in members:
578 # Get the member's type and name
579 info = self.getTypeNameTuple(member)
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)
590 value = result.group(0)
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,
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,
609 feature_protect=self.featureExtraProtect))
610 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
612 # Insert a lock_guard line
613 def lock_guard(self, indent):
614 return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent
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]
621 for member in struct_members:
622 if self.isHandleTypeObject(member.type):
624 elif member.type in struct_member_dict:
625 if self.struct_contains_object(member.type) == True:
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):
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)
640 # Return list of objects from a given list of parameters or members
641 def getObjectsInParameterList(self, item_list, create_func):
643 if create_func == True:
644 member_list = item_list[0:-1]
646 member_list = item_list
647 for item in member_list:
648 if self.isHandleTypeObject(paramtype.text):
649 object_list.add(item)
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:
658 for item in struct.members[1].extstructs.split(','):
659 if item != '' and self.struct_contains_object(item) == 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)
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:
676 # Generate VulkanObjectType from object type
677 def GetVulkanObjType(self, type):
678 return 'kVulkanObjectType%s' % type[2:]
680 # Return correct dispatch table type -- instance or device
681 def GetDispType(self, type):
682 return 'instance' if type in ['VkInstance', 'VkPhysicalDevice'] else 'device'
684 # Generate source for creating a Vulkan object
685 def generate_create_object_code(self, indent, proto, params, cmd_info):
687 handle_type = params[-1].find('type')
688 if self.isHandleTypeObject(handle_type.text):
689 # Check for special case where multiple handles are returned
691 if cmd_info[-1].len is not None:
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
710 # Generate source for destroying a non-dispatchable object
711 def generate_destroy_object_code(self, indent, proto, cmd_info):
712 destroy_obj_code = ''
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:
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'
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
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):
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
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
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):
769 index = 'index%s' % str(array_index)
771 # Process any objects in this structure and recurse for any sub-structs in this struct
772 for member in members:
774 if member.iscreate and first_level_param and member == members[-1]:
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)
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]
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)
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
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)
819 post_code += tmp_post
820 indent = self.decIndent(indent)
821 pre_code += '%s }\n' % indent
822 return decls, pre_code, post_code
824 # For a particular API, generate the object handling code
825 def generate_wrapping_code(self, cmd):
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)
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)
842 destroy_array = False
843 destroy_object_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
855 param_pre_code += destroy_object_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
861 # Capture command parameter info needed to create, destroy, and validate objects
862 def genCmd(self, cmdinfo, cmdname, alias):
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
869 for member in members:
870 len = self.getLen(member)
873 struct_member_dict = dict(self.structMembers)
874 # Generate member info
876 constains_extension_structs = False
877 for member in members:
878 # Get type and name of member
879 info = self.getTypeNameTuple(member)
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
890 if self.isHandleTypeObject(type) == True:
891 if (len is not None) and (isconst == 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:
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,
904 isoptional=self.paramIsOptional(member),
907 extstructs=extstructs,
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))
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:
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:]) ]
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:
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'):
955 if (resulttype != None):
956 assignresult = resulttype.text + ' result = '
959 # Pre-pend declarations and pre-api-call codegen
961 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n")))
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;')
981 raise Exception('Unknown result type ' + resulttype.text)
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' ]