1 #!/usr/bin/python3 -i 2 # 3 # Copyright (c) 2015-2016 The Khronos Group Inc. 4 # Copyright (c) 2015-2016 Valve Corporation 5 # Copyright (c) 2015-2016 LunarG, Inc. 6 # Copyright (c) 2015-2016 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: Tobin Ehlis <tobine (at] google.com> 21 # Author: Mark Lobodzinski <mark (at] lunarg.com> 22 23 import os,re,sys 24 import xml.etree.ElementTree as etree 25 from generator import * 26 from collections import namedtuple 27 28 # UniqueObjectsGeneratorOptions - subclass of GeneratorOptions. 29 # 30 # Adds options used by UniqueObjectsOutputGenerator objects during 31 # unique objects layer generation. 32 # 33 # Additional members 34 # prefixText - list of strings to prefix generated header with 35 # (usually a copyright statement + calling convention macros). 36 # protectFile - True if multiple inclusion protection should be 37 # generated (based on the filename) around the entire header. 38 # protectFeature - True if #ifndef..#endif protection should be 39 # generated around a feature interface in the header file. 40 # genFuncPointers - True if function pointer typedefs should be 41 # generated 42 # protectProto - If conditional protection should be generated 43 # around prototype declarations, set to either '#ifdef' 44 # to require opt-in (#ifdef protectProtoStr) or '#ifndef' 45 # to require opt-out (#ifndef protectProtoStr). Otherwise 46 # set to None. 47 # protectProtoStr - #ifdef/#ifndef symbol to use around prototype 48 # declarations, if protectProto is set 49 # apicall - string to use for the function declaration prefix, 50 # such as APICALL on Windows. 51 # apientry - string to use for the calling convention macro, 52 # in typedefs, such as APIENTRY. 53 # apientryp - string to use for the calling convention macro 54 # in function pointer typedefs, such as APIENTRYP. 55 # indentFuncProto - True if prototype declarations should put each 56 # parameter on a separate line 57 # indentFuncPointer - True if typedefed function pointers should put each 58 # parameter on a separate line 59 # alignFuncParam - if nonzero and parameters are being put on a 60 # separate line, align parameter names at the specified column 61 class UniqueObjectsGeneratorOptions(GeneratorOptions): 62 def __init__(self, 63 filename = None, 64 directory = '.', 65 apiname = None, 66 profile = None, 67 versions = '.*', 68 emitversions = '.*', 69 defaultExtensions = None, 70 addExtensions = None, 71 removeExtensions = None, 72 sortProcedure = regSortFeatures, 73 prefixText = "", 74 genFuncPointers = True, 75 protectFile = True, 76 protectFeature = True, 77 protectProto = None, 78 protectProtoStr = None, 79 apicall = '', 80 apientry = '', 81 apientryp = '', 82 indentFuncProto = True, 83 indentFuncPointer = False, 84 alignFuncParam = 0): 85 GeneratorOptions.__init__(self, filename, directory, apiname, profile, 86 versions, emitversions, defaultExtensions, 87 addExtensions, removeExtensions, sortProcedure) 88 self.prefixText = prefixText 89 self.genFuncPointers = genFuncPointers 90 self.protectFile = protectFile 91 self.protectFeature = protectFeature 92 self.protectProto = protectProto 93 self.protectProtoStr = protectProtoStr 94 self.apicall = apicall 95 self.apientry = apientry 96 self.apientryp = apientryp 97 self.indentFuncProto = indentFuncProto 98 self.indentFuncPointer = indentFuncPointer 99 self.alignFuncParam = alignFuncParam 100 101 # UniqueObjectsOutputGenerator - subclass of OutputGenerator. 102 # Generates unique objects layer non-dispatchable handle-wrapping code. 103 # 104 # ---- methods ---- 105 # UniqueObjectsOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state. 106 # ---- methods overriding base class ---- 107 # beginFile(genOpts) 108 # endFile() 109 # beginFeature(interface, emit) 110 # endFeature() 111 # genCmd(cmdinfo) 112 # genStruct() 113 # genType() 114 class UniqueObjectsOutputGenerator(OutputGenerator): 115 """Generate UniqueObjects code based on XML element attributes""" 116 # This is an ordered list of sections in the header file. 117 ALL_SECTIONS = ['command'] 118 def __init__(self, 119 errFile = sys.stderr, 120 warnFile = sys.stderr, 121 diagFile = sys.stdout): 122 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 123 self.INDENT_SPACES = 4 124 self.intercepts = [] 125 self.instance_extensions = [] 126 self.device_extensions = [] 127 # Commands which are not autogenerated but still intercepted 128 self.no_autogen_list = [ 129 'vkGetDeviceProcAddr', 130 'vkGetInstanceProcAddr', 131 'vkCreateInstance', 132 'vkDestroyInstance', 133 'vkCreateDevice', 134 'vkDestroyDevice', 135 'vkCreateComputePipelines', 136 'vkCreateGraphicsPipelines', 137 'vkCreateSwapchainKHR', 138 'vkCreateSharedSwapchainsKHR', 139 'vkGetSwapchainImagesKHR', 140 'vkQueuePresentKHR', 141 'vkEnumerateInstanceLayerProperties', 142 'vkEnumerateDeviceLayerProperties', 143 'vkEnumerateInstanceExtensionProperties', 144 'vkCreateDescriptorUpdateTemplateKHR', 145 'vkDestroyDescriptorUpdateTemplateKHR', 146 'vkUpdateDescriptorSetWithTemplateKHR', 147 'vkCmdPushDescriptorSetWithTemplateKHR', 148 'vkDebugMarkerSetObjectTagEXT', 149 'vkDebugMarkerSetObjectNameEXT', 150 'vkGetPhysicalDeviceDisplayProperties2KHR', 151 'vkGetPhysicalDeviceDisplayPlaneProperties2KHR', 152 'vkGetDisplayModeProperties2KHR', 153 'vkCreateRenderPass', 154 'vkDestroyRenderPass', 155 ] 156 # Commands shadowed by interface functions and are not implemented 157 self.interface_functions = [ 158 'vkGetPhysicalDeviceDisplayPropertiesKHR', 159 'vkGetPhysicalDeviceDisplayPlanePropertiesKHR', 160 'vkGetDisplayPlaneSupportedDisplaysKHR', 161 'vkGetDisplayModePropertiesKHR', 162 'vkGetDisplayPlaneCapabilitiesKHR', 163 # DebugReport APIs are hooked, but handled separately in the source file 164 'vkCreateDebugReportCallbackEXT', 165 'vkDestroyDebugReportCallbackEXT', 166 'vkDebugReportMessageEXT', 167 ] 168 self.headerVersion = None 169 # Internal state - accumulators for different inner block text 170 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 171 172 self.cmdMembers = [] 173 self.cmd_feature_protect = [] # Save ifdef's for each command 174 self.cmd_info_data = [] # Save the cmdinfo data for wrapping the handles when processing is complete 175 self.structMembers = [] # List of StructMemberData records for all Vulkan structs 176 self.extension_structs = [] # List of all structs or sister-structs containing handles 177 # A sister-struct may contain no handles but shares a structextends attribute with one that does 178 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType 179 self.struct_member_dict = dict() 180 # Named tuples to store struct and command data 181 self.StructType = namedtuple('StructType', ['name', 'value']) 182 self.CmdMemberData = namedtuple('CmdMemberData', ['name', 'members']) 183 self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo']) 184 self.CmdExtraProtect = namedtuple('CmdExtraProtect', ['name', 'extra_protect']) 185 186 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isconst', 'iscount', 'len', 'extstructs', 'cdecl', 'islocal', 'iscreate', 'isdestroy', 'feature_protect']) 187 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members']) 188 # 189 def incIndent(self, indent): 190 inc = ' ' * self.INDENT_SPACES 191 if indent: 192 return indent + inc 193 return inc 194 # 195 def decIndent(self, indent): 196 if indent and (len(indent) > self.INDENT_SPACES): 197 return indent[:-self.INDENT_SPACES] 198 return '' 199 # 200 # Override makeProtoName to drop the "vk" prefix 201 def makeProtoName(self, name, tail): 202 return self.genOpts.apientry + name[2:] + tail 203 # 204 # Check if the parameter passed in is a pointer to an array 205 def paramIsArray(self, param): 206 return param.attrib.get('len') is not None 207 # 208 def beginFile(self, genOpts): 209 OutputGenerator.beginFile(self, genOpts) 210 # User-supplied prefix text, if any (list of strings) 211 if (genOpts.prefixText): 212 for s in genOpts.prefixText: 213 write(s, file=self.outFile) 214 # Namespace 215 self.newline() 216 write('namespace unique_objects {', file = self.outFile) 217 # Now that the data is all collected and complete, generate and output the wrapping/unwrapping routines 218 def endFile(self): 219 220 self.struct_member_dict = dict(self.structMembers) 221 222 # Generate the list of APIs that might need to handle wrapped extension structs 223 self.GenerateCommandWrapExtensionList() 224 # Write out wrapping/unwrapping functions 225 self.WrapCommands() 226 # Build and write out pNext processing function 227 extension_proc = self.build_extension_processing_func() 228 self.newline() 229 write('// Unique Objects pNext extension handling function', file=self.outFile) 230 write('%s' % extension_proc, file=self.outFile) 231 232 # Actually write the interface to the output file. 233 if (self.emit): 234 self.newline() 235 if (self.featureExtraProtect != None): 236 write('#ifdef', self.featureExtraProtect, file=self.outFile) 237 # Write the unique_objects code to the file 238 if (self.sections['command']): 239 if (self.genOpts.protectProto): 240 write(self.genOpts.protectProto, 241 self.genOpts.protectProtoStr, file=self.outFile) 242 write('\n'.join(self.sections['command']), end=u'', file=self.outFile) 243 if (self.featureExtraProtect != None): 244 write('\n#endif //', self.featureExtraProtect, file=self.outFile) 245 else: 246 self.newline() 247 248 # Record intercepted procedures 249 write('// Map of all APIs to be intercepted by this layer', file=self.outFile) 250 write('static const std::unordered_map<std::string, void*> name_to_funcptr_map = {', file=self.outFile) 251 write('\n'.join(self.intercepts), file=self.outFile) 252 write('};\n', file=self.outFile) 253 self.newline() 254 write('} // namespace unique_objects', file=self.outFile) 255 # Finish processing in superclass 256 OutputGenerator.endFile(self) 257 # 258 def beginFeature(self, interface, emit): 259 # Start processing in superclass 260 OutputGenerator.beginFeature(self, interface, emit) 261 self.headerVersion = None 262 263 if self.featureName != 'VK_VERSION_1_0': 264 white_list_entry = [] 265 if (self.featureExtraProtect != None): 266 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ] 267 white_list_entry += [ '"%s"' % self.featureName ] 268 if (self.featureExtraProtect != None): 269 white_list_entry += [ '#endif' ] 270 featureType = interface.get('type') 271 if featureType == 'instance': 272 self.instance_extensions += white_list_entry 273 elif featureType == 'device': 274 self.device_extensions += white_list_entry 275 # 276 def endFeature(self): 277 # Finish processing in superclass 278 OutputGenerator.endFeature(self) 279 # 280 def genType(self, typeinfo, name): 281 OutputGenerator.genType(self, typeinfo, name) 282 typeElem = typeinfo.elem 283 # If the type is a struct type, traverse the imbedded <member> tags generating a structure. 284 # Otherwise, emit the tag text. 285 category = typeElem.get('category') 286 if (category == 'struct' or category == 'union'): 287 self.genStruct(typeinfo, name) 288 # 289 # Append a definition to the specified section 290 def appendSection(self, section, text): 291 # self.sections[section].append('SECTION: ' + section + '\n') 292 self.sections[section].append(text) 293 # 294 # Check if the parameter passed in is a pointer 295 def paramIsPointer(self, param): 296 ispointer = False 297 for elem in param: 298 if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail: 299 ispointer = True 300 return ispointer 301 # 302 # Get the category of a type 303 def getTypeCategory(self, typename): 304 types = self.registry.tree.findall("types/type") 305 for elem in types: 306 if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename: 307 return elem.attrib.get('category') 308 # 309 # Check if a parent object is dispatchable or not 310 def isHandleTypeNonDispatchable(self, handletype): 311 handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']") 312 if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE': 313 return True 314 else: 315 return False 316 # 317 # Retrieve the type and name for a parameter 318 def getTypeNameTuple(self, param): 319 type = '' 320 name = '' 321 for elem in param: 322 if elem.tag == 'type': 323 type = noneStr(elem.text) 324 elif elem.tag == 'name': 325 name = noneStr(elem.text) 326 return (type, name) 327 # 328 # Retrieve the value of the len tag 329 def getLen(self, param): 330 result = None 331 len = param.attrib.get('len') 332 if len and len != 'null-terminated': 333 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we 334 # have a null terminated array of strings. We strip the null-terminated from the 335 # 'len' field and only return the parameter specifying the string count 336 if 'null-terminated' in len: 337 result = len.split(',')[0] 338 else: 339 result = len 340 # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol 341 result = str(result).replace('::', '->') 342 return result 343 # 344 # Generate a VkStructureType based on a structure typename 345 def genVkStructureType(self, typename): 346 # Add underscore between lowercase then uppercase 347 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename) 348 # Change to uppercase 349 value = value.upper() 350 # Add STRUCTURE_TYPE_ 351 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value) 352 # 353 # Struct parameter check generation. 354 # This is a special case of the <type> tag where the contents are interpreted as a set of 355 # <member> tags instead of freeform C type declarations. The <member> tags are just like 356 # <param> tags - they are a declaration of a struct or union member. Only simple member 357 # declarations are supported (no nested structs etc.) 358 def genStruct(self, typeinfo, typeName): 359 OutputGenerator.genStruct(self, typeinfo, typeName) 360 members = typeinfo.elem.findall('.//member') 361 # Iterate over members once to get length parameters for arrays 362 lens = set() 363 for member in members: 364 len = self.getLen(member) 365 if len: 366 lens.add(len) 367 # Generate member info 368 membersInfo = [] 369 for member in members: 370 # Get the member's type and name 371 info = self.getTypeNameTuple(member) 372 type = info[0] 373 name = info[1] 374 cdecl = self.makeCParamDecl(member, 0) 375 # Process VkStructureType 376 if type == 'VkStructureType': 377 # Extract the required struct type value from the comments 378 # embedded in the original text defining the 'typeinfo' element 379 rawXml = etree.tostring(typeinfo.elem).decode('ascii') 380 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml) 381 if result: 382 value = result.group(0) 383 else: 384 value = self.genVkStructureType(typeName) 385 # Store the required type value 386 self.structTypes[typeName] = self.StructType(name=name, value=value) 387 # Store pointer/array/string info 388 extstructs = self.registry.validextensionstructs[typeName] if name == 'pNext' else None 389 membersInfo.append(self.CommandParam(type=type, 390 name=name, 391 ispointer=self.paramIsPointer(member), 392 isconst=True if 'const' in cdecl else False, 393 iscount=True if name in lens else False, 394 len=self.getLen(member), 395 extstructs=extstructs, 396 cdecl=cdecl, 397 islocal=False, 398 iscreate=False, 399 isdestroy=False, 400 feature_protect=self.featureExtraProtect)) 401 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo)) 402 403 # 404 # Insert a lock_guard line 405 def lock_guard(self, indent): 406 return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent 407 # 408 # Determine if a struct has an NDO as a member or an embedded member 409 def struct_contains_ndo(self, struct_item): 410 struct_member_dict = dict(self.structMembers) 411 struct_members = struct_member_dict[struct_item] 412 413 for member in struct_members: 414 if self.isHandleTypeNonDispatchable(member.type): 415 return True 416 elif member.type in struct_member_dict: 417 if self.struct_contains_ndo(member.type) == True: 418 return True 419 return False 420 # 421 # Return list of struct members which contain, or which sub-structures contain 422 # an NDO in a given list of parameters or members 423 def getParmeterStructsWithNdos(self, item_list): 424 struct_list = set() 425 for item in item_list: 426 paramtype = item.find('type') 427 typecategory = self.getTypeCategory(paramtype.text) 428 if typecategory == 'struct': 429 if self.struct_contains_ndo(paramtype.text) == True: 430 struct_list.add(item) 431 return struct_list 432 # 433 # Return list of non-dispatchable objects from a given list of parameters or members 434 def getNdosInParameterList(self, item_list, create_func): 435 ndo_list = set() 436 if create_func == True: 437 member_list = item_list[0:-1] 438 else: 439 member_list = item_list 440 for item in member_list: 441 if self.isHandleTypeNonDispatchable(paramtype.text): 442 ndo_list.add(item) 443 return ndo_list 444 # 445 # Construct list of extension structs containing handles, or extension structs that share a structextends attribute 446 # WITH an extension struct containing handles. All extension structs in any pNext chain will have to be copied. 447 # TODO: make this recursive -- structs buried three or more levels deep are not searched for extensions 448 def GenerateCommandWrapExtensionList(self): 449 for struct in self.structMembers: 450 if (len(struct.members) > 1) and struct.members[1].extstructs is not None: 451 found = False; 452 for item in struct.members[1].extstructs: 453 if item != '' and self.struct_contains_ndo(item) == True: 454 found = True 455 if found == True: 456 for item in struct.members[1].extstructs: 457 if item != '' and item not in self.extension_structs: 458 self.extension_structs.append(item) 459 # 460 # Returns True if a struct may have a pNext chain containing an NDO 461 def StructWithExtensions(self, struct_type): 462 if struct_type in self.struct_member_dict: 463 param_info = self.struct_member_dict[struct_type] 464 if (len(param_info) > 1) and param_info[1].extstructs is not None: 465 for item in param_info[1].extstructs: 466 if item in self.extension_structs: 467 return True 468 return False 469 # 470 # Generate pNext handling function 471 def build_extension_processing_func(self): 472 # Construct helper functions to build and free pNext extension chains 473 pnext_proc = '' 474 pnext_proc += 'void *CreateUnwrappedExtensionStructs(layer_data *dev_data, const void *pNext) {\n' 475 pnext_proc += ' void *cur_pnext = const_cast<void *>(pNext);\n' 476 pnext_proc += ' void *head_pnext = NULL;\n' 477 pnext_proc += ' void *prev_ext_struct = NULL;\n' 478 pnext_proc += ' void *cur_ext_struct = NULL;\n\n' 479 pnext_proc += ' while (cur_pnext != NULL) {\n' 480 pnext_proc += ' GenericHeader *header = reinterpret_cast<GenericHeader *>(cur_pnext);\n\n' 481 pnext_proc += ' switch (header->sType) {\n' 482 for item in self.extension_structs: 483 struct_info = self.struct_member_dict[item] 484 if struct_info[0].feature_protect is not None: 485 pnext_proc += '#ifdef %s \n' % struct_info[0].feature_protect 486 pnext_proc += ' case %s: {\n' % self.structTypes[item].value 487 pnext_proc += ' safe_%s *safe_struct = new safe_%s;\n' % (item, item) 488 pnext_proc += ' safe_struct->initialize(reinterpret_cast<const %s *>(cur_pnext));\n' % item 489 # Generate code to unwrap the handles 490 indent = ' ' 491 (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, 'safe_struct->', 0, False, False, False, False) 492 pnext_proc += tmp_pre 493 pnext_proc += ' cur_ext_struct = reinterpret_cast<void *>(safe_struct);\n' 494 pnext_proc += ' } break;\n' 495 if struct_info[0].feature_protect is not None: 496 pnext_proc += '#endif // %s \n' % struct_info[0].feature_protect 497 pnext_proc += '\n' 498 pnext_proc += ' default:\n' 499 pnext_proc += ' break;\n' 500 pnext_proc += ' }\n\n' 501 pnext_proc += ' // Save pointer to the first structure in the pNext chain\n' 502 pnext_proc += ' head_pnext = (head_pnext ? head_pnext : cur_ext_struct);\n\n' 503 pnext_proc += ' // For any extension structure but the first, link the last struct\'s pNext to the current ext struct\n' 504 pnext_proc += ' if (prev_ext_struct) {\n' 505 pnext_proc += ' (reinterpret_cast<GenericHeader *>(prev_ext_struct))->pNext = cur_ext_struct;\n' 506 pnext_proc += ' }\n' 507 pnext_proc += ' prev_ext_struct = cur_ext_struct;\n\n' 508 pnext_proc += ' // Process the next structure in the chain\n' 509 pnext_proc += ' cur_pnext = const_cast<void *>(header->pNext);\n' 510 pnext_proc += ' }\n' 511 pnext_proc += ' return head_pnext;\n' 512 pnext_proc += '}\n\n' 513 pnext_proc += '// Free a pNext extension chain\n' 514 pnext_proc += 'void FreeUnwrappedExtensionStructs(void *head) {\n' 515 pnext_proc += ' void * curr_ptr = head;\n' 516 pnext_proc += ' while (curr_ptr) {\n' 517 pnext_proc += ' GenericHeader *header = reinterpret_cast<GenericHeader *>(curr_ptr);\n' 518 pnext_proc += ' void *temp = curr_ptr;\n' 519 pnext_proc += ' curr_ptr = header->pNext;\n' 520 pnext_proc += ' free(temp);\n' 521 pnext_proc += ' }\n' 522 pnext_proc += '}\n' 523 return pnext_proc 524 525 # 526 # Generate source for creating a non-dispatchable object 527 def generate_create_ndo_code(self, indent, proto, params, cmd_info): 528 create_ndo_code = '' 529 handle_type = params[-1].find('type') 530 if self.isHandleTypeNonDispatchable(handle_type.text): 531 # Check for special case where multiple handles are returned 532 ndo_array = False 533 if cmd_info[-1].len is not None: 534 ndo_array = True; 535 handle_name = params[-1].find('name') 536 create_ndo_code += '%sif (VK_SUCCESS == result) {\n' % (indent) 537 indent = self.incIndent(indent) 538 create_ndo_code += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent) 539 ndo_dest = '*%s' % handle_name.text 540 if ndo_array == True: 541 create_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[-1].len) 542 indent = self.incIndent(indent) 543 ndo_dest = '%s[index0]' % cmd_info[-1].name 544 create_ndo_code += '%s%s = WrapNew(dev_data, %s);\n' % (indent, ndo_dest, ndo_dest) 545 if ndo_array == True: 546 indent = self.decIndent(indent) 547 create_ndo_code += '%s}\n' % indent 548 indent = self.decIndent(indent) 549 create_ndo_code += '%s}\n' % (indent) 550 return create_ndo_code 551 # 552 # Generate source for destroying a non-dispatchable object 553 def generate_destroy_ndo_code(self, indent, proto, cmd_info): 554 destroy_ndo_code = '' 555 ndo_array = False 556 if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]: 557 # Check for special case where multiple handles are returned 558 if cmd_info[-1].len is not None: 559 ndo_array = True; 560 param = -1 561 else: 562 param = -2 563 if self.isHandleTypeNonDispatchable(cmd_info[param].type) == True: 564 if ndo_array == True: 565 # This API is freeing an array of handles. Remove them from the unique_id map. 566 destroy_ndo_code += '%sif ((VK_SUCCESS == result) && (%s)) {\n' % (indent, cmd_info[param].name) 567 indent = self.incIndent(indent) 568 destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent) 569 destroy_ndo_code += '%sfor (uint32_t index0 = 0; index0 < %s; index0++) {\n' % (indent, cmd_info[param].len) 570 indent = self.incIndent(indent) 571 destroy_ndo_code += '%s%s handle = %s[index0];\n' % (indent, cmd_info[param].type, cmd_info[param].name) 572 destroy_ndo_code += '%suint64_t unique_id = reinterpret_cast<uint64_t &>(handle);\n' % (indent) 573 destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(unique_id);\n' % (indent) 574 indent = self.decIndent(indent); 575 destroy_ndo_code += '%s}\n' % indent 576 indent = self.decIndent(indent); 577 destroy_ndo_code += '%s}\n' % indent 578 else: 579 # Remove a single handle from the map 580 destroy_ndo_code += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent) 581 destroy_ndo_code += '%suint64_t %s_id = reinterpret_cast<uint64_t &>(%s);\n' % (indent, cmd_info[param].name, cmd_info[param].name) 582 destroy_ndo_code += '%s%s = (%s)dev_data->unique_id_mapping[%s_id];\n' % (indent, cmd_info[param].name, cmd_info[param].type, cmd_info[param].name) 583 destroy_ndo_code += '%sdev_data->unique_id_mapping.erase(%s_id);\n' % (indent, cmd_info[param].name) 584 destroy_ndo_code += '%slock.unlock();\n' % (indent) 585 return ndo_array, destroy_ndo_code 586 587 # 588 # Clean up local declarations 589 def cleanUpLocalDeclarations(self, indent, prefix, name, len, index, process_pnext): 590 cleanup = '%sif (local_%s%s) {\n' % (indent, prefix, name) 591 if len is not None: 592 if process_pnext: 593 cleanup += '%s for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, len, index) 594 cleanup += '%s FreeUnwrappedExtensionStructs(const_cast<void *>(local_%s%s[%s].pNext));\n' % (indent, prefix, name, index) 595 cleanup += '%s }\n' % indent 596 cleanup += '%s delete[] local_%s%s;\n' % (indent, prefix, name) 597 else: 598 if process_pnext: 599 cleanup += '%s FreeUnwrappedExtensionStructs(const_cast<void *>(local_%s%s->pNext));\n' % (indent, prefix, name) 600 cleanup += '%s delete local_%s%s;\n' % (indent, prefix, name) 601 cleanup += "%s}\n" % (indent) 602 return cleanup 603 # 604 # Output UO code for a single NDO (ndo_count is NULL) or a counted list of NDOs 605 def outputNDOs(self, ndo_type, ndo_name, ndo_count, prefix, index, indent, destroy_func, destroy_array, top_level): 606 decl_code = '' 607 pre_call_code = '' 608 post_call_code = '' 609 if ndo_count is not None: 610 if top_level == True: 611 decl_code += '%s%s *local_%s%s = NULL;\n' % (indent, ndo_type, prefix, ndo_name) 612 pre_call_code += '%s if (%s%s) {\n' % (indent, prefix, ndo_name) 613 indent = self.incIndent(indent) 614 if top_level == True: 615 pre_call_code += '%s local_%s%s = new %s[%s];\n' % (indent, prefix, ndo_name, ndo_type, ndo_count) 616 pre_call_code += '%s for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index) 617 indent = self.incIndent(indent) 618 pre_call_code += '%s local_%s%s[%s] = Unwrap(dev_data, %s[%s]);\n' % (indent, prefix, ndo_name, index, ndo_name, index) 619 else: 620 pre_call_code += '%s for (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, ndo_count, index) 621 indent = self.incIndent(indent) 622 pre_call_code += '%s %s%s[%s] = Unwrap(dev_data, %s%s[%s]);\n' % (indent, prefix, ndo_name, index, prefix, ndo_name, index) 623 indent = self.decIndent(indent) 624 pre_call_code += '%s }\n' % indent 625 indent = self.decIndent(indent) 626 pre_call_code += '%s }\n' % indent 627 if top_level == True: 628 post_call_code += '%sif (local_%s%s)\n' % (indent, prefix, ndo_name) 629 indent = self.incIndent(indent) 630 post_call_code += '%sdelete[] local_%s;\n' % (indent, ndo_name) 631 else: 632 if top_level == True: 633 if (destroy_func == False) or (destroy_array == True): 634 pre_call_code += '%s %s = Unwrap(dev_data, %s);\n' % (indent, ndo_name, ndo_name) 635 else: 636 # Make temp copy of this var with the 'local' removed. It may be better to not pass in 'local_' 637 # as part of the string and explicitly print it 638 fix = str(prefix).strip('local_'); 639 pre_call_code += '%s if (%s%s) {\n' % (indent, fix, ndo_name) 640 indent = self.incIndent(indent) 641 pre_call_code += '%s %s%s = Unwrap(dev_data, %s%s);\n' % (indent, prefix, ndo_name, fix, ndo_name) 642 indent = self.decIndent(indent) 643 pre_call_code += '%s }\n' % indent 644 return decl_code, pre_call_code, post_call_code 645 # 646 # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct 647 # create_func means that this is API creates or allocates NDOs 648 # destroy_func indicates that this API destroys or frees NDOs 649 # destroy_array means that the destroy_func operated on an array of NDOs 650 def uniquify_members(self, members, indent, prefix, array_index, create_func, destroy_func, destroy_array, first_level_param): 651 decls = '' 652 pre_code = '' 653 post_code = '' 654 index = 'index%s' % str(array_index) 655 array_index += 1 656 # Process any NDOs in this structure and recurse for any sub-structs in this struct 657 for member in members: 658 process_pnext = self.StructWithExtensions(member.type) 659 # Handle NDOs 660 if self.isHandleTypeNonDispatchable(member.type) == True: 661 count_name = member.len 662 if (count_name is not None): 663 if first_level_param == False: 664 count_name = '%s%s' % (prefix, member.len) 665 666 if (first_level_param == False) or (create_func == False): 667 (tmp_decl, tmp_pre, tmp_post) = self.outputNDOs(member.type, member.name, count_name, prefix, index, indent, destroy_func, destroy_array, first_level_param) 668 decls += tmp_decl 669 pre_code += tmp_pre 670 post_code += tmp_post 671 # Handle Structs that contain NDOs at some level 672 elif member.type in self.struct_member_dict: 673 # Structs at first level will have an NDO, OR, we need a safe_struct for the pnext chain 674 if self.struct_contains_ndo(member.type) == True or process_pnext: 675 struct_info = self.struct_member_dict[member.type] 676 # Struct Array 677 if member.len is not None: 678 # Update struct prefix 679 if first_level_param == True: 680 new_prefix = 'local_%s' % member.name 681 # Declare safe_VarType for struct 682 decls += '%ssafe_%s *%s = NULL;\n' % (indent, member.type, new_prefix) 683 else: 684 new_prefix = '%s%s' % (prefix, member.name) 685 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name) 686 indent = self.incIndent(indent) 687 if first_level_param == True: 688 pre_code += '%s %s = new safe_%s[%s];\n' % (indent, new_prefix, member.type, member.len) 689 pre_code += '%s for (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index) 690 indent = self.incIndent(indent) 691 if first_level_param == True: 692 pre_code += '%s %s[%s].initialize(&%s[%s]);\n' % (indent, new_prefix, index, member.name, index) 693 if process_pnext: 694 pre_code += '%s %s[%s].pNext = CreateUnwrappedExtensionStructs(dev_data, %s[%s].pNext);\n' % (indent, new_prefix, index, new_prefix, index) 695 local_prefix = '%s[%s].' % (new_prefix, index) 696 # Process sub-structs in this struct 697 (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, local_prefix, array_index, create_func, destroy_func, destroy_array, False) 698 decls += tmp_decl 699 pre_code += tmp_pre 700 post_code += tmp_post 701 indent = self.decIndent(indent) 702 pre_code += '%s }\n' % indent 703 indent = self.decIndent(indent) 704 pre_code += '%s }\n' % indent 705 if first_level_param == True: 706 post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len, index, process_pnext) 707 # Single Struct 708 else: 709 # Update struct prefix 710 if first_level_param == True: 711 new_prefix = 'local_%s->' % member.name 712 decls += '%ssafe_%s *local_%s%s = NULL;\n' % (indent, member.type, prefix, member.name) 713 else: 714 new_prefix = '%s%s->' % (prefix, member.name) 715 # Declare safe_VarType for struct 716 pre_code += '%s if (%s%s) {\n' % (indent, prefix, member.name) 717 indent = self.incIndent(indent) 718 if first_level_param == True: 719 pre_code += '%s local_%s%s = new safe_%s(%s);\n' % (indent, prefix, member.name, member.type, member.name) 720 # Process sub-structs in this struct 721 (tmp_decl, tmp_pre, tmp_post) = self.uniquify_members(struct_info, indent, new_prefix, array_index, create_func, destroy_func, destroy_array, False) 722 decls += tmp_decl 723 pre_code += tmp_pre 724 post_code += tmp_post 725 if process_pnext: 726 pre_code += '%s local_%s%s->pNext = CreateUnwrappedExtensionStructs(dev_data, local_%s%s->pNext);\n' % (indent, prefix, member.name, prefix, member.name) 727 indent = self.decIndent(indent) 728 pre_code += '%s }\n' % indent 729 if first_level_param == True: 730 post_code += self.cleanUpLocalDeclarations(indent, prefix, member.name, member.len, index, process_pnext) 731 return decls, pre_code, post_code 732 # 733 # For a particular API, generate the non-dispatchable-object wrapping/unwrapping code 734 def generate_wrapping_code(self, cmd): 735 indent = ' ' 736 proto = cmd.find('proto/name') 737 params = cmd.findall('param') 738 739 if proto.text is not None: 740 cmd_member_dict = dict(self.cmdMembers) 741 cmd_info = cmd_member_dict[proto.text] 742 # Handle ndo create/allocate operations 743 if cmd_info[0].iscreate: 744 create_ndo_code = self.generate_create_ndo_code(indent, proto, params, cmd_info) 745 else: 746 create_ndo_code = '' 747 # Handle ndo destroy/free operations 748 if cmd_info[0].isdestroy: 749 (destroy_array, destroy_ndo_code) = self.generate_destroy_ndo_code(indent, proto, cmd_info) 750 else: 751 destroy_array = False 752 destroy_ndo_code = '' 753 paramdecl = '' 754 param_pre_code = '' 755 param_post_code = '' 756 create_func = True if create_ndo_code else False 757 destroy_func = True if destroy_ndo_code else False 758 (paramdecl, param_pre_code, param_post_code) = self.uniquify_members(cmd_info, indent, '', 0, create_func, destroy_func, destroy_array, True) 759 param_post_code += create_ndo_code 760 if destroy_ndo_code: 761 if destroy_array == True: 762 param_post_code += destroy_ndo_code 763 else: 764 param_pre_code += destroy_ndo_code 765 if param_pre_code: 766 if (not destroy_func) or (destroy_array): 767 param_pre_code = '%s{\n%s%s%s%s}\n' % (' ', indent, self.lock_guard(indent), param_pre_code, indent) 768 return paramdecl, param_pre_code, param_post_code 769 # 770 # Capture command parameter info needed to wrap NDOs as well as handling some boilerplate code 771 def genCmd(self, cmdinfo, cmdname): 772 773 # Add struct-member type information to command parameter information 774 OutputGenerator.genCmd(self, cmdinfo, cmdname) 775 members = cmdinfo.elem.findall('.//param') 776 # Iterate over members once to get length parameters for arrays 777 lens = set() 778 for member in members: 779 len = self.getLen(member) 780 if len: 781 lens.add(len) 782 struct_member_dict = dict(self.structMembers) 783 # Generate member info 784 membersInfo = [] 785 constains_extension_structs = False 786 for member in members: 787 # Get type and name of member 788 info = self.getTypeNameTuple(member) 789 type = info[0] 790 name = info[1] 791 cdecl = self.makeCParamDecl(member, 0) 792 # Check for parameter name in lens set 793 iscount = True if name in lens else False 794 len = self.getLen(member) 795 isconst = True if 'const' in cdecl else False 796 ispointer = self.paramIsPointer(member) 797 # Mark param as local if it is an array of NDOs 798 islocal = False; 799 if self.isHandleTypeNonDispatchable(type) == True: 800 if (len is not None) and (isconst == True): 801 islocal = True 802 # Or if it's a struct that contains an NDO 803 elif type in struct_member_dict: 804 if self.struct_contains_ndo(type) == True: 805 islocal = True 806 isdestroy = True if True in [destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']] else False 807 iscreate = True if True in [create_txt in cmdname for create_txt in ['Create', 'Allocate', 'GetRandROutputDisplayEXT', 'RegisterDeviceEvent', 'RegisterDisplayEvent']] else False 808 extstructs = self.registry.validextensionstructs[type] if name == 'pNext' else None 809 membersInfo.append(self.CommandParam(type=type, 810 name=name, 811 ispointer=ispointer, 812 isconst=isconst, 813 iscount=iscount, 814 len=len, 815 extstructs=extstructs, 816 cdecl=cdecl, 817 islocal=islocal, 818 iscreate=iscreate, 819 isdestroy=isdestroy, 820 feature_protect=self.featureExtraProtect)) 821 self.cmdMembers.append(self.CmdMemberData(name=cmdname, members=membersInfo)) 822 self.cmd_info_data.append(self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo)) 823 self.cmd_feature_protect.append(self.CmdExtraProtect(name=cmdname, extra_protect=self.featureExtraProtect)) 824 # 825 # Create code to wrap NDOs as well as handling some boilerplate code 826 def WrapCommands(self): 827 cmd_member_dict = dict(self.cmdMembers) 828 cmd_info_dict = dict(self.cmd_info_data) 829 cmd_protect_dict = dict(self.cmd_feature_protect) 830 831 for api_call in self.cmdMembers: 832 cmdname = api_call.name 833 cmdinfo = cmd_info_dict[api_call.name] 834 if cmdname in self.interface_functions: 835 continue 836 if cmdname in self.no_autogen_list: 837 decls = self.makeCDecls(cmdinfo.elem) 838 self.appendSection('command', '') 839 self.appendSection('command', '// Declare only') 840 self.appendSection('command', decls[0]) 841 self.intercepts += [ ' {"%s", (void *)%s},' % (cmdname,cmdname[2:]) ] 842 continue 843 # Generate NDO wrapping/unwrapping code for all parameters 844 (api_decls, api_pre, api_post) = self.generate_wrapping_code(cmdinfo.elem) 845 # If API doesn't contain an NDO's, don't fool with it 846 if not api_decls and not api_pre and not api_post: 847 continue 848 feature_extra_protect = cmd_protect_dict[api_call.name] 849 if (feature_extra_protect != None): 850 self.appendSection('command', '') 851 self.appendSection('command', '#ifdef '+ feature_extra_protect) 852 self.intercepts += [ '#ifdef %s' % feature_extra_protect ] 853 # Add intercept to procmap 854 self.intercepts += [ ' {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ] 855 decls = self.makeCDecls(cmdinfo.elem) 856 self.appendSection('command', '') 857 self.appendSection('command', decls[0][:-1]) 858 self.appendSection('command', '{') 859 # Setup common to call wrappers, first parameter is always dispatchable 860 dispatchable_type = cmdinfo.elem.find('param/type').text 861 dispatchable_name = cmdinfo.elem.find('param/name').text 862 # Generate local instance/pdev/device data lookup 863 if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]: 864 self.appendSection('command', ' instance_layer_data *dev_data = GetLayerDataPtr(get_dispatch_key('+dispatchable_name+'), instance_layer_data_map);') 865 else: 866 self.appendSection('command', ' layer_data *dev_data = GetLayerDataPtr(get_dispatch_key('+dispatchable_name+'), layer_data_map);') 867 # Handle return values, if any 868 resulttype = cmdinfo.elem.find('proto/type') 869 if (resulttype != None and resulttype.text == 'void'): 870 resulttype = None 871 if (resulttype != None): 872 assignresult = resulttype.text + ' result = ' 873 else: 874 assignresult = '' 875 # Pre-pend declarations and pre-api-call codegen 876 if api_decls: 877 self.appendSection('command', "\n".join(str(api_decls).rstrip().split("\n"))) 878 if api_pre: 879 self.appendSection('command', "\n".join(str(api_pre).rstrip().split("\n"))) 880 # Generate the API call itself 881 # Gather the parameter items 882 params = cmdinfo.elem.findall('param/name') 883 # Pull out the text for each of the parameters, separate them by commas in a list 884 paramstext = ', '.join([str(param.text) for param in params]) 885 # If any of these paramters has been replaced by a local var, fix up the list 886 params = cmd_member_dict[cmdname] 887 for param in params: 888 if param.islocal == True or self.StructWithExtensions(param.type): 889 if param.ispointer == True: 890 paramstext = paramstext.replace(param.name, '(%s %s*)local_%s' % ('const', param.type, param.name)) 891 else: 892 paramstext = paramstext.replace(param.name, '(%s %s)local_%s' % ('const', param.type, param.name)) 893 # Use correct dispatch table 894 API = cmdinfo.elem.attrib.get('name').replace('vk','dev_data->dispatch_table.',1) 895 # Put all this together for the final down-chain call 896 self.appendSection('command', ' ' + assignresult + API + '(' + paramstext + ');') 897 # And add the post-API-call codegen 898 self.appendSection('command', "\n".join(str(api_post).rstrip().split("\n"))) 899 # Handle the return result variable, if any 900 if (resulttype != None): 901 self.appendSection('command', ' return result;') 902 self.appendSection('command', '}') 903 if (feature_extra_protect != None): 904 self.appendSection('command', '#endif // '+ feature_extra_protect) 905 self.intercepts += [ '#endif' ] 906