1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2015-2019 The Khronos Group Inc. 4# Copyright (c) 2015-2019 Valve Corporation 5# Copyright (c) 2015-2019 LunarG, Inc. 6# Copyright (c) 2015-2019 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# Author: Dave Houlton <daveh@lunarg.com> 22 23import os,re,sys,string,json 24import xml.etree.ElementTree as etree 25from generator import * 26from collections import namedtuple 27from common_codegen import * 28 29# This is a workaround to use a Python 2.7 and 3.x compatible syntax. 30from io import open 31 32# ObjectTrackerGeneratorOptions - subclass of GeneratorOptions. 33# 34# Adds options used by ObjectTrackerOutputGenerator objects during 35# object_tracker layer generation. 36# 37# Additional members 38# prefixText - list of strings to prefix generated header with 39# (usually a copyright statement + calling convention macros). 40# protectFile - True if multiple inclusion protection should be 41# generated (based on the filename) around the entire header. 42# protectFeature - True if #ifndef..#endif protection should be 43# generated around a feature interface in the header file. 44# genFuncPointers - True if function pointer typedefs should be 45# generated 46# protectProto - If conditional protection should be generated 47# around prototype declarations, set to either '#ifdef' 48# to require opt-in (#ifdef protectProtoStr) or '#ifndef' 49# to require opt-out (#ifndef protectProtoStr). Otherwise 50# set to None. 51# protectProtoStr - #ifdef/#ifndef symbol to use around prototype 52# declarations, if protectProto is set 53# apicall - string to use for the function declaration prefix, 54# such as APICALL on Windows. 55# apientry - string to use for the calling convention macro, 56# in typedefs, such as APIENTRY. 57# apientryp - string to use for the calling convention macro 58# in function pointer typedefs, such as APIENTRYP. 59# indentFuncProto - True if prototype declarations should put each 60# parameter on a separate line 61# indentFuncPointer - True if typedefed function pointers should put each 62# parameter on a separate line 63# alignFuncParam - if nonzero and parameters are being put on a 64# separate line, align parameter names at the specified column 65class ObjectTrackerGeneratorOptions(GeneratorOptions): 66 def __init__(self, 67 conventions = None, 68 filename = None, 69 directory = '.', 70 apiname = None, 71 profile = None, 72 versions = '.*', 73 emitversions = '.*', 74 defaultExtensions = None, 75 addExtensions = None, 76 removeExtensions = None, 77 emitExtensions = None, 78 sortProcedure = regSortFeatures, 79 prefixText = "", 80 genFuncPointers = True, 81 protectFile = True, 82 protectFeature = True, 83 apicall = '', 84 apientry = '', 85 apientryp = '', 86 indentFuncProto = True, 87 indentFuncPointer = False, 88 alignFuncParam = 0, 89 expandEnumerants = True, 90 valid_usage_path = ''): 91 GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile, 92 versions, emitversions, defaultExtensions, 93 addExtensions, removeExtensions, emitExtensions, sortProcedure) 94 self.prefixText = prefixText 95 self.genFuncPointers = genFuncPointers 96 self.protectFile = protectFile 97 self.protectFeature = protectFeature 98 self.apicall = apicall 99 self.apientry = apientry 100 self.apientryp = apientryp 101 self.indentFuncProto = indentFuncProto 102 self.indentFuncPointer = indentFuncPointer 103 self.alignFuncParam = alignFuncParam 104 self.expandEnumerants = expandEnumerants 105 self.valid_usage_path = valid_usage_path 106 107 108# ObjectTrackerOutputGenerator - subclass of OutputGenerator. 109# Generates object_tracker layer object validation code 110# 111# ---- methods ---- 112# ObjectTrackerOutputGenerator(errFile, warnFile, diagFile) - args as for OutputGenerator. Defines additional internal state. 113# ---- methods overriding base class ---- 114# beginFile(genOpts) 115# endFile() 116# beginFeature(interface, emit) 117# endFeature() 118# genCmd(cmdinfo) 119# genStruct() 120# genType() 121class ObjectTrackerOutputGenerator(OutputGenerator): 122 """Generate ObjectTracker code based on XML element attributes""" 123 # This is an ordered list of sections in the header file. 124 ALL_SECTIONS = ['command'] 125 def __init__(self, 126 errFile = sys.stderr, 127 warnFile = sys.stderr, 128 diagFile = sys.stdout): 129 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 130 self.INDENT_SPACES = 4 131 self.prototypes = [] 132 self.instance_extensions = [] 133 self.device_extensions = [] 134 # Commands which are not autogenerated but still intercepted 135 self.no_autogen_list = [ 136 'vkDestroyInstance', 137 'vkCreateInstance', 138 'vkEnumeratePhysicalDevices', 139 'vkGetPhysicalDeviceQueueFamilyProperties', 140 'vkGetPhysicalDeviceQueueFamilyProperties2', 141 'vkGetPhysicalDeviceQueueFamilyProperties2KHR', 142 'vkGetDeviceQueue', 143 'vkGetDeviceQueue2', 144 'vkCreateDescriptorSetLayout', 145 'vkDestroyDescriptorPool', 146 'vkDestroyCommandPool', 147 'vkAllocateCommandBuffers', 148 'vkAllocateDescriptorSets', 149 'vkFreeDescriptorSets', 150 'vkFreeCommandBuffers', 151 'vkUpdateDescriptorSets', 152 'vkBeginCommandBuffer', 153 'vkGetDescriptorSetLayoutSupport', 154 'vkGetDescriptorSetLayoutSupportKHR', 155 'vkDestroySwapchainKHR', 156 'vkGetSwapchainImagesKHR', 157 'vkCmdPushDescriptorSetKHR', 158 'vkDestroyDevice', 159 'vkResetDescriptorPool', 160 'vkGetPhysicalDeviceDisplayPropertiesKHR', 161 'vkGetPhysicalDeviceDisplayProperties2KHR', 162 'vkGetDisplayModePropertiesKHR', 163 'vkGetDisplayModeProperties2KHR', 164 'vkAcquirePerformanceConfigurationINTEL', 165 'vkReleasePerformanceConfigurationINTEL', 166 'vkQueueSetPerformanceConfigurationINTEL', 167 'vkCreateFramebuffer', 168 ] 169 # These VUIDS are not implicit, but are best handled in this layer. Codegen for vkDestroy calls will generate a key 170 # which is translated here into a good VU. Saves ~40 checks. 171 self.manual_vuids = dict() 172 self.manual_vuids = { 173 "fence-compatalloc": "\"VUID-vkDestroyFence-fence-01121\"", 174 "fence-nullalloc": "\"VUID-vkDestroyFence-fence-01122\"", 175 "event-compatalloc": "\"VUID-vkDestroyEvent-event-01146\"", 176 "event-nullalloc": "\"VUID-vkDestroyEvent-event-01147\"", 177 "buffer-compatalloc": "\"VUID-vkDestroyBuffer-buffer-00923\"", 178 "buffer-nullalloc": "\"VUID-vkDestroyBuffer-buffer-00924\"", 179 "image-compatalloc": "\"VUID-vkDestroyImage-image-01001\"", 180 "image-nullalloc": "\"VUID-vkDestroyImage-image-01002\"", 181 "shaderModule-compatalloc": "\"VUID-vkDestroyShaderModule-shaderModule-01092\"", 182 "shaderModule-nullalloc": "\"VUID-vkDestroyShaderModule-shaderModule-01093\"", 183 "pipeline-compatalloc": "\"VUID-vkDestroyPipeline-pipeline-00766\"", 184 "pipeline-nullalloc": "\"VUID-vkDestroyPipeline-pipeline-00767\"", 185 "sampler-compatalloc": "\"VUID-vkDestroySampler-sampler-01083\"", 186 "sampler-nullalloc": "\"VUID-vkDestroySampler-sampler-01084\"", 187 "renderPass-compatalloc": "\"VUID-vkDestroyRenderPass-renderPass-00874\"", 188 "renderPass-nullalloc": "\"VUID-vkDestroyRenderPass-renderPass-00875\"", 189 "descriptorUpdateTemplate-compatalloc": "\"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00356\"", 190 "descriptorUpdateTemplate-nullalloc": "\"VUID-vkDestroyDescriptorUpdateTemplate-descriptorSetLayout-00357\"", 191 "imageView-compatalloc": "\"VUID-vkDestroyImageView-imageView-01027\"", 192 "imageView-nullalloc": "\"VUID-vkDestroyImageView-imageView-01028\"", 193 "pipelineCache-compatalloc": "\"VUID-vkDestroyPipelineCache-pipelineCache-00771\"", 194 "pipelineCache-nullalloc": "\"VUID-vkDestroyPipelineCache-pipelineCache-00772\"", 195 "pipelineLayout-compatalloc": "\"VUID-vkDestroyPipelineLayout-pipelineLayout-00299\"", 196 "pipelineLayout-nullalloc": "\"VUID-vkDestroyPipelineLayout-pipelineLayout-00300\"", 197 "descriptorSetLayout-compatalloc": "\"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00284\"", 198 "descriptorSetLayout-nullalloc": "\"VUID-vkDestroyDescriptorSetLayout-descriptorSetLayout-00285\"", 199 "semaphore-compatalloc": "\"VUID-vkDestroySemaphore-semaphore-01138\"", 200 "semaphore-nullalloc": "\"VUID-vkDestroySemaphore-semaphore-01139\"", 201 "queryPool-compatalloc": "\"VUID-vkDestroyQueryPool-queryPool-00794\"", 202 "queryPool-nullalloc": "\"VUID-vkDestroyQueryPool-queryPool-00795\"", 203 "bufferView-compatalloc": "\"VUID-vkDestroyBufferView-bufferView-00937\"", 204 "bufferView-nullalloc": "\"VUID-vkDestroyBufferView-bufferView-00938\"", 205 "surface-compatalloc": "\"VUID-vkDestroySurfaceKHR-surface-01267\"", 206 "surface-nullalloc": "\"VUID-vkDestroySurfaceKHR-surface-01268\"", 207 "framebuffer-compatalloc": "\"VUID-vkDestroyFramebuffer-framebuffer-00893\"", 208 "framebuffer-nullalloc": "\"VUID-vkDestroyFramebuffer-framebuffer-00894\"", 209 } 210 211 # Commands shadowed by interface functions and are not implemented 212 self.interface_functions = [ 213 ] 214 self.headerVersion = None 215 # Internal state - accumulators for different inner block text 216 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 217 self.cmd_list = [] # list of commands processed to maintain ordering 218 self.cmd_info_dict = {} # Per entry-point data for code generation and validation 219 self.structMembers = [] # List of StructMemberData records for all Vulkan structs 220 self.extension_structs = [] # List of all structs or sister-structs containing handles 221 # A sister-struct may contain no handles but shares <validextensionstructs> with one that does 222 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType 223 self.struct_member_dict = dict() 224 # Named tuples to store struct and command data 225 self.StructType = namedtuple('StructType', ['name', 'value']) 226 self.CmdInfoData = namedtuple('CmdInfoData', ['name', 'cmdinfo', 'members', 'extra_protect', 'alias', 'iscreate', 'isdestroy', 'allocator']) 227 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'isconst', 'isoptional', 'iscount', 'iscreate', 'len', 'extstructs', 'cdecl', 'islocal']) 228 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members']) 229 self.object_types = [] # List of all handle types 230 self.valid_vuids = set() # Set of all valid VUIDs 231 self.vuid_dict = dict() # VUID dictionary (from JSON) 232 # 233 # Check if the parameter passed in is optional 234 def paramIsOptional(self, param): 235 # See if the handle is optional 236 isoptional = False 237 # Simple, if it's optional, return true 238 optString = param.attrib.get('optional') 239 if optString: 240 if optString == 'true': 241 isoptional = True 242 elif ',' in optString: 243 opts = [] 244 for opt in optString.split(','): 245 val = opt.strip() 246 if val == 'true': 247 opts.append(True) 248 elif val == 'false': 249 opts.append(False) 250 else: 251 print('Unrecognized len attribute value',val) 252 isoptional = opts 253 if not isoptional: 254 # Matching logic in parameter validation and ValidityOutputGenerator.isHandleOptional 255 optString = param.attrib.get('noautovalidity') 256 if optString and optString == 'true': 257 isoptional = True; 258 return isoptional 259 # 260 # Get VUID identifier from implicit VUID tag 261 def GetVuid(self, parent, suffix): 262 vuid_string = 'VUID-%s-%s' % (parent, suffix) 263 vuid = "kVUIDUndefined" 264 if '->' in vuid_string: 265 return vuid 266 if vuid_string in self.valid_vuids: 267 vuid = "\"%s\"" % vuid_string 268 else: 269 alias = self.cmd_info_dict[parent].alias if parent in self.cmd_info_dict else None 270 if alias: 271 alias_string = 'VUID-%s-%s' % (alias, suffix) 272 if alias_string in self.valid_vuids: 273 vuid = "\"%s\"" % alias_string 274 return vuid 275 # 276 # Increases indent by 4 spaces and tracks it globally 277 def incIndent(self, indent): 278 inc = ' ' * self.INDENT_SPACES 279 if indent: 280 return indent + inc 281 return inc 282 # 283 # Decreases indent by 4 spaces and tracks it globally 284 def decIndent(self, indent): 285 if indent and (len(indent) > self.INDENT_SPACES): 286 return indent[:-self.INDENT_SPACES] 287 return '' 288 # 289 # Override makeProtoName to drop the "vk" prefix 290 def makeProtoName(self, name, tail): 291 return self.genOpts.apientry + name[2:] + tail 292 # 293 # Check if the parameter passed in is a pointer to an array 294 def paramIsArray(self, param): 295 return param.attrib.get('len') is not None 296 297 # 298 # Generate the object tracker undestroyed object validation function 299 def GenReportFunc(self): 300 output_func = '' 301 output_func += 'bool ObjectLifetimes::ReportUndestroyedObjects(VkDevice device, const std::string& error_code) {\n' 302 output_func += ' bool skip = false;\n' 303 output_func += ' skip |= DeviceReportUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer, error_code);\n' 304 for handle in self.object_types: 305 if self.handle_types.IsNonDispatchable(handle): 306 output_func += ' skip |= DeviceReportUndestroyedObjects(device, %s, error_code);\n' % (self.GetVulkanObjType(handle)) 307 output_func += ' return skip;\n' 308 output_func += '}\n' 309 return output_func 310 311 # 312 # Generate the object tracker undestroyed object destruction function 313 def GenDestroyFunc(self): 314 output_func = '' 315 output_func += 'void ObjectLifetimes::DestroyUndestroyedObjects(VkDevice device) {\n' 316 output_func += ' DeviceDestroyUndestroyedObjects(device, kVulkanObjectTypeCommandBuffer);\n' 317 for handle in self.object_types: 318 if self.handle_types.IsNonDispatchable(handle): 319 output_func += ' DeviceDestroyUndestroyedObjects(device, %s);\n' % (self.GetVulkanObjType(handle)) 320 output_func += '}\n' 321 return output_func 322 323 # 324 # Walk the JSON-derived dict and find all "vuid" key values 325 def ExtractVUIDs(self, d): 326 if hasattr(d, 'items'): 327 for k, v in d.items(): 328 if k == "vuid": 329 yield v 330 elif isinstance(v, dict): 331 for s in self.ExtractVUIDs(v): 332 yield s 333 elif isinstance (v, list): 334 for l in v: 335 for s in self.ExtractVUIDs(l): 336 yield s 337 # 338 # Separate content for validation source and header files 339 def otwrite(self, dest, formatstring): 340 if 'object_tracker.h' in self.genOpts.filename and (dest == 'hdr' or dest == 'both'): 341 write(formatstring, file=self.outFile) 342 elif 'object_tracker.cpp' in self.genOpts.filename and (dest == 'cpp' or dest == 'both'): 343 write(formatstring, file=self.outFile) 344 345 # 346 # Called at beginning of processing as file is opened 347 def beginFile(self, genOpts): 348 OutputGenerator.beginFile(self, genOpts) 349 350 # Initialize members that require the tree 351 self.handle_types = GetHandleTypes(self.registry.tree) 352 self.type_categories = GetTypeCategories(self.registry.tree) 353 354 header_file = (genOpts.filename == 'object_tracker.h') 355 source_file = (genOpts.filename == 'object_tracker.cpp') 356 357 if not header_file and not source_file: 358 print("Error: Output Filenames have changed, update generator source.\n") 359 sys.exit(1) 360 361 self.valid_usage_path = genOpts.valid_usage_path 362 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json') 363 if os.path.isfile(vu_json_filename): 364 json_file = open(vu_json_filename, 'r') 365 self.vuid_dict = json.load(json_file) 366 json_file.close() 367 if len(self.vuid_dict) == 0: 368 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename) 369 sys.exit(1) 370 371 # Build a set of all vuid text strings found in validusage.json 372 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict): 373 self.valid_vuids.add(json_vuid_string) 374 375 # File Comment 376 file_comment = '// *** THIS FILE IS GENERATED - DO NOT EDIT ***\n' 377 file_comment += '// See object_tracker_generator.py for modifications\n' 378 self.otwrite('both', file_comment) 379 # Copyright Statement 380 copyright = '' 381 copyright += '\n' 382 copyright += '/***************************************************************************\n' 383 copyright += ' *\n' 384 copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n' 385 copyright += ' * Copyright (c) 2015-2019 Valve Corporation\n' 386 copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n' 387 copyright += ' * Copyright (c) 2015-2019 Google Inc.\n' 388 copyright += ' *\n' 389 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n' 390 copyright += ' * you may not use this file except in compliance with the License.\n' 391 copyright += ' * You may obtain a copy of the License at\n' 392 copyright += ' *\n' 393 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n' 394 copyright += ' *\n' 395 copyright += ' * Unless required by applicable law or agreed to in writing, software\n' 396 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n' 397 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' 398 copyright += ' * See the License for the specific language governing permissions and\n' 399 copyright += ' * limitations under the License.\n' 400 copyright += ' *\n' 401 copyright += ' * Author: Mark Lobodzinski <mark@lunarg.com>\n' 402 copyright += ' * Author: Dave Houlton <daveh@lunarg.com>\n' 403 copyright += ' *\n' 404 copyright += ' ****************************************************************************/\n' 405 self.otwrite('both', copyright) 406 self.newline() 407 self.otwrite('cpp', '#include "chassis.h"') 408 self.otwrite('cpp', '#include "object_lifetime_validation.h"') 409 410 # 411 # Now that the data is all collected and complete, generate and output the object validation routines 412 def endFile(self): 413 self.struct_member_dict = dict(self.structMembers) 414 # Generate the list of APIs that might need to handle wrapped extension structs 415 # self.GenerateCommandWrapExtensionList() 416 self.WrapCommands() 417 # Build undestroyed objects reporting function 418 report_func = self.GenReportFunc() 419 self.newline() 420 # Build undestroyed objects destruction function 421 destroy_func = self.GenDestroyFunc() 422 self.otwrite('cpp', '\n') 423 self.otwrite('cpp', '// ObjectTracker undestroyed objects validation function') 424 self.otwrite('cpp', '%s' % report_func) 425 self.otwrite('cpp', '%s' % destroy_func) 426 # Actually write the interface to the output file. 427 if (self.emit): 428 self.newline() 429 if self.featureExtraProtect is not None: 430 prot = '#ifdef %s' % self.featureExtraProtect 431 self.otwrite('both', '%s' % prot) 432 # Write the object_tracker code to the file 433 if self.sections['command']: 434 source = ('\n'.join(self.sections['command'])) 435 self.otwrite('both', '%s' % source) 436 if (self.featureExtraProtect is not None): 437 prot = '\n#endif // %s', self.featureExtraProtect 438 self.otwrite('both', prot) 439 else: 440 self.otwrite('both', '\n') 441 442 443 self.otwrite('hdr', 'void PostCallRecordDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator);') 444 self.otwrite('hdr', 'void PreCallRecordResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags);') 445 self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties);') 446 self.otwrite('hdr', 'void PreCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers);') 447 self.otwrite('hdr', 'void PreCallRecordFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets);') 448 self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR *pQueueFamilyProperties);') 449 self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR *pQueueFamilyProperties);') 450 self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties, VkResult result);') 451 self.otwrite('hdr', 'void PostCallRecordGetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties, VkResult result);') 452 self.otwrite('hdr', 'void PostCallRecordGetPhysicalDeviceDisplayProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayProperties2KHR *pProperties, VkResult result);') 453 self.otwrite('hdr', 'void PostCallRecordGetDisplayModeProperties2KHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModeProperties2KHR *pProperties, VkResult result);') 454 OutputGenerator.endFile(self) 455 # 456 # Processing point at beginning of each extension definition 457 def beginFeature(self, interface, emit): 458 # Start processing in superclass 459 OutputGenerator.beginFeature(self, interface, emit) 460 self.headerVersion = None 461 self.featureExtraProtect = GetFeatureProtect(interface) 462 463 if self.featureName != 'VK_VERSION_1_0' and self.featureName != 'VK_VERSION_1_1': 464 white_list_entry = [] 465 if (self.featureExtraProtect is not None): 466 white_list_entry += [ '#ifdef %s' % self.featureExtraProtect ] 467 white_list_entry += [ '"%s"' % self.featureName ] 468 if (self.featureExtraProtect is not None): 469 white_list_entry += [ '#endif' ] 470 featureType = interface.get('type') 471 if featureType == 'instance': 472 self.instance_extensions += white_list_entry 473 elif featureType == 'device': 474 self.device_extensions += white_list_entry 475 # 476 # Processing point at end of each extension definition 477 def endFeature(self): 478 # Finish processing in superclass 479 OutputGenerator.endFeature(self) 480 # 481 # Process enums, structs, etc. 482 def genType(self, typeinfo, name, alias): 483 OutputGenerator.genType(self, typeinfo, name, alias) 484 typeElem = typeinfo.elem 485 # If the type is a struct type, traverse the imbedded <member> tags generating a structure. 486 # Otherwise, emit the tag text. 487 category = typeElem.get('category') 488 if (category == 'struct' or category == 'union'): 489 self.genStruct(typeinfo, name, alias) 490 if category == 'handle': 491 self.object_types.append(name) 492 # 493 # Append a definition to the specified section 494 def appendSection(self, section, text): 495 # self.sections[section].append('SECTION: ' + section + '\n') 496 self.sections[section].append(text) 497 # 498 # Check if the parameter passed in is a pointer 499 def paramIsPointer(self, param): 500 ispointer = False 501 for elem in param: 502 if elem.tag == 'type' and elem.tail is not None and '*' in elem.tail: 503 ispointer = True 504 return ispointer 505 # 506 # Retrieve the type and name for a parameter 507 def getTypeNameTuple(self, param): 508 type = '' 509 name = '' 510 for elem in param: 511 if elem.tag == 'type': 512 type = noneStr(elem.text) 513 elif elem.tag == 'name': 514 name = noneStr(elem.text) 515 return (type, name) 516 # 517 # Retrieve the value of the len tag 518 def getLen(self, param): 519 result = None 520 len = param.attrib.get('len') 521 if len and len != 'null-terminated': 522 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we 523 # have a null terminated array of strings. We strip the null-terminated from the 524 # 'len' field and only return the parameter specifying the string count 525 if 'null-terminated' in len: 526 result = len.split(',')[0] 527 else: 528 result = len 529 # Spec has now notation for len attributes, using :: instead of platform specific pointer symbol 530 result = str(result).replace('::', '->') 531 return result 532 # 533 # Generate a VkStructureType based on a structure typename 534 def genVkStructureType(self, typename): 535 # Add underscore between lowercase then uppercase 536 value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename) 537 # Change to uppercase 538 value = value.upper() 539 # Add STRUCTURE_TYPE_ 540 return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value) 541 # 542 # Struct parameter check generation. 543 # This is a special case of the <type> tag where the contents are interpreted as a set of 544 # <member> tags instead of freeform C type declarations. The <member> tags are just like 545 # <param> tags - they are a declaration of a struct or union member. Only simple member 546 # declarations are supported (no nested structs etc.) 547 def genStruct(self, typeinfo, typeName, alias): 548 OutputGenerator.genStruct(self, typeinfo, typeName, alias) 549 members = typeinfo.elem.findall('.//member') 550 # Iterate over members once to get length parameters for arrays 551 lens = set() 552 for member in members: 553 len = self.getLen(member) 554 if len: 555 lens.add(len) 556 # Generate member info 557 membersInfo = [] 558 for member in members: 559 # Get the member's type and name 560 info = self.getTypeNameTuple(member) 561 type = info[0] 562 name = info[1] 563 cdecl = self.makeCParamDecl(member, 0) 564 # Process VkStructureType 565 if type == 'VkStructureType': 566 # Extract the required struct type value from the comments 567 # embedded in the original text defining the 'typeinfo' element 568 rawXml = etree.tostring(typeinfo.elem).decode('ascii') 569 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml) 570 if result: 571 value = result.group(0) 572 else: 573 value = self.genVkStructureType(typeName) 574 # Store the required type value 575 self.structTypes[typeName] = self.StructType(name=name, value=value) 576 # Store pointer/array/string info 577 extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None 578 membersInfo.append(self.CommandParam(type=type, 579 name=name, 580 isconst=True if 'const' in cdecl else False, 581 isoptional=self.paramIsOptional(member), 582 iscount=True if name in lens else False, 583 len=self.getLen(member), 584 extstructs=extstructs, 585 cdecl=cdecl, 586 islocal=False, 587 iscreate=False)) 588 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo)) 589 # 590 # Insert a lock_guard line 591 def lock_guard(self, indent): 592 return '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % indent 593 # 594 # Determine if a struct has an object as a member or an embedded member 595 def struct_contains_object(self, struct_item): 596 struct_member_dict = dict(self.structMembers) 597 struct_members = struct_member_dict[struct_item] 598 599 for member in struct_members: 600 if member.type in self.handle_types: 601 return True 602 # recurse for member structs, guard against infinite recursion 603 elif member.type in struct_member_dict and member.type != struct_item: 604 if self.struct_contains_object(member.type): 605 return True 606 return False 607 # 608 # Return list of struct members which contain, or whose sub-structures contain an obj in a given list of parameters or members 609 def getParmeterStructsWithObjects(self, item_list): 610 struct_list = set() 611 for item in item_list: 612 paramtype = item.find('type') 613 typecategory = self.type_categories[paramtype.text] 614 if typecategory == 'struct': 615 if self.struct_contains_object(paramtype.text) == True: 616 struct_list.add(item) 617 return struct_list 618 # 619 # Return list of objects from a given list of parameters or members 620 def getObjectsInParameterList(self, item_list, create_func): 621 object_list = set() 622 if create_func == True: 623 member_list = item_list[0:-1] 624 else: 625 member_list = item_list 626 for item in member_list: 627 if paramtype.text in self.handle_types: 628 object_list.add(item) 629 return object_list 630 # 631 # Construct list of extension structs containing handles, or extension structs that share a <validextensionstructs> 632 # tag WITH an extension struct containing handles. 633 def GenerateCommandWrapExtensionList(self): 634 for struct in self.structMembers: 635 if (len(struct.members) > 1) and struct.members[1].extstructs is not None: 636 found = False; 637 for item in struct.members[1].extstructs.split(','): 638 if item != '' and self.struct_contains_object(item) == True: 639 found = True 640 if found == True: 641 for item in struct.members[1].extstructs.split(','): 642 if item != '' and item not in self.extension_structs: 643 self.extension_structs.append(item) 644 # 645 # Returns True if a struct may have a pNext chain containing an object 646 def StructWithExtensions(self, struct_type): 647 if struct_type in self.struct_member_dict: 648 param_info = self.struct_member_dict[struct_type] 649 if (len(param_info) > 1) and param_info[1].extstructs is not None: 650 for item in param_info[1].extstructs.split(','): 651 if item in self.extension_structs: 652 return True 653 return False 654 # 655 # Generate VulkanObjectType from object type 656 def GetVulkanObjType(self, type): 657 return 'kVulkanObjectType%s' % type[2:] 658 # 659 # Return correct dispatch table type -- instance or device 660 def GetDispType(self, type): 661 return 'instance' if type in ['VkInstance', 'VkPhysicalDevice'] else 'device' 662 # 663 # Generate source for creating a Vulkan object 664 def generate_create_object_code(self, indent, proto, params, cmd_info, allocator): 665 create_obj_code = '' 666 handle_type = params[-1].find('type') 667 is_create_pipelines = False 668 669 if handle_type.text in self.handle_types: 670 # Check for special case where multiple handles are returned 671 object_array = False 672 if cmd_info[-1].len is not None: 673 object_array = True; 674 handle_name = params[-1].find('name') 675 object_dest = '*%s' % handle_name.text 676 if object_array == True: 677 if 'CreateGraphicsPipelines' in proto.text or 'CreateComputePipelines' in proto.text or 'CreateRayTracingPipelines' in proto.text: 678 is_create_pipelines = True 679 create_obj_code += '%sif (VK_ERROR_VALIDATION_FAILED_EXT == result) return;\n' % indent 680 create_obj_code += '%sif (%s) {\n' % (indent, handle_name.text) 681 indent = self.incIndent(indent) 682 countispointer = '' 683 if 'uint32_t*' in cmd_info[-2].cdecl: 684 countispointer = '*' 685 create_obj_code += '%sfor (uint32_t index = 0; index < %s%s; index++) {\n' % (indent, countispointer, cmd_info[-1].len) 686 indent = self.incIndent(indent) 687 object_dest = '%s[index]' % cmd_info[-1].name 688 689 dispobj = params[0].find('type').text 690 if is_create_pipelines: 691 create_obj_code += '%sif (!pPipelines[index]) continue;\n' % indent 692 create_obj_code += '%sCreateObject(%s, %s, %s, %s);\n' % (indent, params[0].find('name').text, object_dest, self.GetVulkanObjType(cmd_info[-1].type), allocator) 693 if object_array == True: 694 indent = self.decIndent(indent) 695 create_obj_code += '%s}\n' % indent 696 indent = self.decIndent(indent) 697 create_obj_code += '%s}\n' % indent 698 indent = self.decIndent(indent) 699 700 return create_obj_code 701 # 702 # Generate source for destroying a non-dispatchable object 703 def generate_destroy_object_code(self, indent, proto, cmd_info): 704 validate_code = '' 705 record_code = '' 706 object_array = False 707 if True in [destroy_txt in proto.text for destroy_txt in ['Destroy', 'Free']]: 708 # Check for special case where multiple handles are returned 709 if cmd_info[-1].len is not None: 710 object_array = True; 711 param = -1 712 else: 713 param = -2 714 compatalloc_vuid_string = '%s-compatalloc' % cmd_info[param].name 715 nullalloc_vuid_string = '%s-nullalloc' % cmd_info[param].name 716 compatalloc_vuid = self.manual_vuids.get(compatalloc_vuid_string, "kVUIDUndefined") 717 nullalloc_vuid = self.manual_vuids.get(nullalloc_vuid_string, "kVUIDUndefined") 718 if cmd_info[param].type in self.handle_types: 719 if object_array == True: 720 # This API is freeing an array of handles -- add loop control 721 validate_code += 'HEY, NEED TO DESTROY AN ARRAY\n' 722 else: 723 dispobj = cmd_info[0].type 724 # Call Destroy a single time 725 validate_code += '%sskip |= ValidateDestroyObject(%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) 726 record_code += '%sRecordDestroyObject(%s, %s, %s);\n' % (indent, cmd_info[0].name, cmd_info[param].name, self.GetVulkanObjType(cmd_info[param].type)) 727 return object_array, validate_code, record_code 728 # 729 # Output validation for a single object (obj_count is NULL) or a counted list of objects 730 def outputObjects(self, obj_type, obj_name, obj_count, prefix, index, indent, disp_name, parent_name, null_allowed, top_level): 731 pre_call_code = '' 732 param_suffix = '%s-parameter' % (obj_name) 733 parent_suffix = '%s-parent' % (obj_name) 734 param_vuid = self.GetVuid(parent_name, param_suffix) 735 parent_vuid = self.GetVuid(parent_name, parent_suffix) 736 737 # If no parent VUID for this member, look for a commonparent VUID 738 if parent_vuid == 'kVUIDUndefined': 739 parent_vuid = self.GetVuid(parent_name, 'commonparent') 740 if obj_count is not None: 741 742 pre_call_code += '%sif (%s%s) {\n' % (indent, prefix, obj_name) 743 indent = self.incIndent(indent) 744 pre_call_code += '%sfor (uint32_t %s = 0; %s < %s; ++%s) {\n' % (indent, index, index, obj_count, index) 745 indent = self.incIndent(indent) 746 pre_call_code += '%sskip |= 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) 747 indent = self.decIndent(indent) 748 pre_call_code += '%s}\n' % indent 749 indent = self.decIndent(indent) 750 pre_call_code += '%s}\n' % indent 751 else: 752 pre_call_code += '%sskip |= 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) 753 return pre_call_code 754 # 755 # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct 756 def validate_objects(self, members, indent, prefix, array_index, disp_name, parent_name, first_level_param): 757 pre_code = '' 758 index = 'index%s' % str(array_index) 759 array_index += 1 760 # Process any objects in this structure and recurse for any sub-structs in this struct 761 for member in members: 762 # Handle objects 763 if member.iscreate and first_level_param and member == members[-1]: 764 continue 765 if member.type in self.handle_types: 766 count_name = member.len 767 if (count_name is not None): 768 count_name = '%s%s' % (prefix, member.len) 769 null_allowed = member.isoptional 770 tmp_pre = self.outputObjects(member.type, member.name, count_name, prefix, index, indent, disp_name, parent_name, str(null_allowed).lower(), first_level_param) 771 pre_code += tmp_pre 772 # Handle Structs that contain objects at some level 773 elif member.type in self.struct_member_dict: 774 # Structs at first level will have an object 775 if self.struct_contains_object(member.type) == True: 776 struct_info = self.struct_member_dict[member.type] 777 # TODO (jbolz): Can this use paramIsPointer? 778 ispointer = '*' in member.cdecl; 779 # Struct Array 780 if member.len is not None: 781 # Update struct prefix 782 new_prefix = '%s%s' % (prefix, member.name) 783 pre_code += '%sif (%s%s) {\n' % (indent, prefix, member.name) 784 indent = self.incIndent(indent) 785 pre_code += '%sfor (uint32_t %s = 0; %s < %s%s; ++%s) {\n' % (indent, index, index, prefix, member.len, index) 786 indent = self.incIndent(indent) 787 local_prefix = '%s[%s].' % (new_prefix, index) 788 # Process sub-structs in this struct 789 tmp_pre = self.validate_objects(struct_info, indent, local_prefix, array_index, disp_name, member.type, False) 790 pre_code += tmp_pre 791 indent = self.decIndent(indent) 792 pre_code += '%s}\n' % indent 793 indent = self.decIndent(indent) 794 pre_code += '%s}\n' % indent 795 # Single Struct Pointer 796 elif ispointer: 797 # Update struct prefix 798 new_prefix = '%s%s->' % (prefix, member.name) 799 # Declare safe_VarType for struct 800 pre_code += '%sif (%s%s) {\n' % (indent, prefix, member.name) 801 indent = self.incIndent(indent) 802 # Process sub-structs in this struct 803 tmp_pre = self.validate_objects(struct_info, indent, new_prefix, array_index, disp_name, member.type, False) 804 pre_code += tmp_pre 805 indent = self.decIndent(indent) 806 pre_code += '%s}\n' % indent 807 # Single Nested Struct 808 else: 809 # Update struct prefix 810 new_prefix = '%s%s.' % (prefix, member.name) 811 # Process sub-structs 812 tmp_pre = self.validate_objects(struct_info, indent, new_prefix, array_index, disp_name, member.type, False) 813 pre_code += tmp_pre 814 return pre_code 815 # 816 # For a particular API, generate the object handling code 817 def generate_wrapping_code(self, cmd): 818 indent = ' ' 819 pre_call_validate = '' 820 pre_call_record = '' 821 post_call_record = '' 822 823 destroy_array = False 824 validate_destroy_code = '' 825 record_destroy_code = '' 826 827 proto = cmd.find('proto/name') 828 params = cmd.findall('param') 829 if proto.text is not None: 830 cmddata = self.cmd_info_dict[proto.text] 831 cmd_info = cmddata.members 832 disp_name = cmd_info[0].name 833 # Handle object create operations if last parameter is created by this call 834 if cmddata.iscreate: 835 post_call_record += self.generate_create_object_code(indent, proto, params, cmd_info, cmddata.allocator) 836 # Handle object destroy operations 837 if cmddata.isdestroy: 838 (destroy_array, validate_destroy_code, record_destroy_code) = self.generate_destroy_object_code(indent, proto, cmd_info) 839 840 pre_call_record += record_destroy_code 841 pre_call_validate += self.validate_objects(cmd_info, indent, '', 0, disp_name, proto.text, True) 842 pre_call_validate += validate_destroy_code 843 844 return pre_call_validate, pre_call_record, post_call_record 845 # 846 # Capture command parameter info needed to create, destroy, and validate objects 847 def genCmd(self, cmdinfo, cmdname, alias): 848 # Add struct-member type information to command parameter information 849 OutputGenerator.genCmd(self, cmdinfo, cmdname, alias) 850 members = cmdinfo.elem.findall('.//param') 851 # Iterate over members once to get length parameters for arrays 852 lens = set() 853 for member in members: 854 length = self.getLen(member) 855 if length: 856 lens.add(length) 857 struct_member_dict = dict(self.structMembers) 858 859 # Set command invariant information needed at a per member level in validate... 860 is_create_command = any(filter(lambda pat: pat in cmdname, ('Create', 'Allocate', 'Enumerate', 'RegisterDeviceEvent', 'RegisterDisplayEvent'))) 861 last_member_is_pointer = len(members) and self.paramIsPointer(members[-1]) 862 iscreate = is_create_command or ('vkGet' in cmdname and last_member_is_pointer) 863 isdestroy = any([destroy_txt in cmdname for destroy_txt in ['Destroy', 'Free']]) 864 865 # Generate member info 866 membersInfo = [] 867 allocator = 'nullptr' 868 for member in members: 869 # Get type and name of member 870 info = self.getTypeNameTuple(member) 871 type = info[0] 872 name = info[1] 873 cdecl = self.makeCParamDecl(member, 0) 874 # Check for parameter name in lens set 875 iscount = True if name in lens else False 876 length = self.getLen(member) 877 isconst = True if 'const' in cdecl else False 878 # Mark param as local if it is an array of objects 879 islocal = False; 880 if type in self.handle_types: 881 if (length is not None) and (isconst == True): 882 islocal = True 883 # Or if it's a struct that contains an object 884 elif type in struct_member_dict: 885 if self.struct_contains_object(type) == True: 886 islocal = True 887 if type == 'VkAllocationCallbacks': 888 allocator = name 889 extstructs = member.attrib.get('validextensionstructs') if name == 'pNext' else None 890 membersInfo.append(self.CommandParam(type=type, 891 name=name, 892 isconst=isconst, 893 isoptional=self.paramIsOptional(member), 894 iscount=iscount, 895 len=length, 896 extstructs=extstructs, 897 cdecl=cdecl, 898 islocal=islocal, 899 iscreate=iscreate)) 900 901 self.cmd_list.append(cmdname) 902 self.cmd_info_dict[cmdname] =self.CmdInfoData(name=cmdname, cmdinfo=cmdinfo, members=membersInfo, iscreate=iscreate, isdestroy=isdestroy, allocator=allocator, extra_protect=self.featureExtraProtect, alias=alias) 903 # 904 # Create code Create, Destroy, and validate Vulkan objects 905 def WrapCommands(self): 906 for cmdname in self.cmd_list: 907 cmddata = self.cmd_info_dict[cmdname] 908 cmdinfo = cmddata.cmdinfo 909 if cmdname in self.interface_functions: 910 continue 911 manual = False 912 if cmdname in self.no_autogen_list: 913 manual = True 914 915 # Generate object handling code 916 (pre_call_validate, pre_call_record, post_call_record) = self.generate_wrapping_code(cmdinfo.elem) 917 918 feature_extra_protect = cmddata.extra_protect 919 if (feature_extra_protect is not None): 920 self.appendSection('command', '') 921 self.appendSection('command', '#ifdef '+ feature_extra_protect) 922 self.prototypes += [ '#ifdef %s' % feature_extra_protect ] 923 924 # Add intercept to procmap 925 self.prototypes += [ ' {"%s", (void*)%s},' % (cmdname,cmdname[2:]) ] 926 927 decls = self.makeCDecls(cmdinfo.elem) 928 929 # Gather the parameter items 930 params = cmdinfo.elem.findall('param/name') 931 # Pull out the text for each of the parameters, separate them by commas in a list 932 paramstext = ', '.join([str(param.text) for param in params]) 933 # Generate the API call template 934 fcn_call = cmdinfo.elem.attrib.get('name').replace('vk', 'TOKEN', 1) + '(' + paramstext + ');' 935 936 func_decl_template = decls[0][:-1].split('VKAPI_CALL ') 937 func_decl_template = func_decl_template[1] 938 939 result_type = cmdinfo.elem.find('proto/type') 940 941 if 'object_tracker.h' in self.genOpts.filename: 942 # Output PreCallValidateAPI prototype if necessary 943 if pre_call_validate: 944 pre_cv_func_decl = 'bool PreCallValidate' + func_decl_template + ';' 945 self.appendSection('command', pre_cv_func_decl) 946 947 # Output PreCallRecordAPI prototype if necessary 948 if pre_call_record: 949 pre_cr_func_decl = 'void PreCallRecord' + func_decl_template + ';' 950 self.appendSection('command', pre_cr_func_decl) 951 952 # Output PosCallRecordAPI prototype if necessary 953 if post_call_record: 954 post_cr_func_decl = 'void PostCallRecord' + func_decl_template + ';' 955 if result_type.text == 'VkResult': 956 post_cr_func_decl = post_cr_func_decl.replace(')', ',\n VkResult result)') 957 self.appendSection('command', post_cr_func_decl) 958 959 if 'object_tracker.cpp' in self.genOpts.filename: 960 # Output PreCallValidateAPI function if necessary 961 if pre_call_validate and not manual: 962 pre_cv_func_decl = 'bool ObjectLifetimes::PreCallValidate' + func_decl_template + ' {' 963 self.appendSection('command', '') 964 self.appendSection('command', pre_cv_func_decl) 965 self.appendSection('command', ' bool skip = false;') 966 self.appendSection('command', pre_call_validate) 967 self.appendSection('command', ' return skip;') 968 self.appendSection('command', '}') 969 970 # Output PreCallRecordAPI function if necessary 971 if pre_call_record and not manual: 972 pre_cr_func_decl = 'void ObjectLifetimes::PreCallRecord' + func_decl_template + ' {' 973 self.appendSection('command', '') 974 self.appendSection('command', pre_cr_func_decl) 975 self.appendSection('command', pre_call_record) 976 self.appendSection('command', '}') 977 978 # Output PosCallRecordAPI function if necessary 979 if post_call_record and not manual: 980 post_cr_func_decl = 'void ObjectLifetimes::PostCallRecord' + func_decl_template + ' {' 981 self.appendSection('command', '') 982 983 if result_type.text == 'VkResult': 984 post_cr_func_decl = post_cr_func_decl.replace(')', ',\n VkResult result)') 985 # The two createpipelines APIs may create on failure -- skip the success result check 986 if 'CreateGraphicsPipelines' not in cmdname and 'CreateComputePipelines' not in cmdname and 'CreateRayTracingPipelines' not in cmdname: 987 post_cr_func_decl = post_cr_func_decl.replace('{', '{\n if (result != VK_SUCCESS) return;') 988 self.appendSection('command', post_cr_func_decl) 989 990 self.appendSection('command', post_call_record) 991 self.appendSection('command', '}') 992 993 if (feature_extra_protect is not None): 994 self.appendSection('command', '#endif // '+ feature_extra_protect) 995 self.prototypes += [ '#endif' ] 996