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