1 #!/usr/bin/env python3 2 # 3 # VK 4 # 5 # Copyright (c) 2015-2016 The Khronos Group Inc. 6 # Copyright (c) 2015-2016 Valve Corporation 7 # Copyright (c) 2015-2016 LunarG, Inc. 8 # Copyright (c) 2015-2016 Google Inc. 9 # 10 # Licensed under the Apache License, Version 2.0 (the "License"); 11 # you may not use this file except in compliance with the License. 12 # You may obtain a copy of the License at 13 # 14 # http://www.apache.org/licenses/LICENSE-2.0 15 # 16 # Unless required by applicable law or agreed to in writing, software 17 # distributed under the License is distributed on an "AS IS" BASIS, 18 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 # See the License for the specific language governing permissions and 20 # limitations under the License. 21 # 22 # Author: Tobin Ehlis <tobine (at] google.com> 23 # Author: Courtney Goeltzenleuchter <courtneygo (at] google.com> 24 # Author: Jon Ashburn <jon (at] lunarg.com> 25 # Author: Mark Lobodzinski <mark (at] lunarg.com> 26 # Author: Mike Stroyan <stroyan (at] google.com> 27 # Author: Tony Barbour <tony (at] LunarG.com> 28 # Author: Chia-I Wu <olv (at] google.com> 29 # Author: Gwan-gyeong Mun <kk.moon (at] samsung.com> 30 31 import sys 32 import os 33 import re 34 35 import vulkan 36 import vk_helper 37 from source_line_info import sourcelineinfo 38 from collections import defaultdict 39 40 def proto_is_global(proto): 41 global_function_names = [ 42 "CreateInstance", 43 "EnumerateInstanceLayerProperties", 44 "EnumerateInstanceExtensionProperties", 45 "EnumerateDeviceLayerProperties", 46 "EnumerateDeviceExtensionProperties", 47 "CreateXcbSurfaceKHR", 48 "GetPhysicalDeviceXcbPresentationSupportKHR", 49 "CreateXlibSurfaceKHR", 50 "GetPhysicalDeviceXlibPresentationSupportKHR", 51 "CreateWaylandSurfaceKHR", 52 "GetPhysicalDeviceWaylandPresentationSupportKHR", 53 "CreateMirSurfaceKHR", 54 "GetPhysicalDeviceMirPresentationSupportKHR", 55 "CreateAndroidSurfaceKHR", 56 "CreateWin32SurfaceKHR", 57 "GetPhysicalDeviceWin32PresentationSupportKHR" 58 ] 59 if proto.params[0].ty == "VkInstance" or proto.params[0].ty == "VkPhysicalDevice" or proto.name in global_function_names: 60 return True 61 else: 62 return False 63 64 def wsi_name(ext_name): 65 wsi_prefix = "" 66 if 'Xcb' in ext_name: 67 wsi_prefix = 'XCB' 68 elif 'Xlib' in ext_name: 69 wsi_prefix = 'XLIB' 70 elif 'Win32' in ext_name: 71 wsi_prefix = 'WIN32' 72 elif 'Mir' in ext_name: 73 wsi_prefix = 'MIR' 74 elif 'Wayland' in ext_name: 75 wsi_prefix = 'WAYLAND' 76 elif 'Android' in ext_name: 77 wsi_prefix = 'ANDROID' 78 else: 79 wsi_prefix = '' 80 return wsi_prefix 81 82 def wsi_ifdef(ext_name): 83 wsi_prefix = wsi_name(ext_name) 84 if not wsi_prefix: 85 return '' 86 else: 87 return "#ifdef VK_USE_PLATFORM_%s_KHR" % wsi_prefix 88 89 def wsi_endif(ext_name): 90 wsi_prefix = wsi_name(ext_name) 91 if not wsi_prefix: 92 return '' 93 else: 94 return "#endif // VK_USE_PLATFORM_%s_KHR" % wsi_prefix 95 96 def generate_get_proc_addr_check(name): 97 return " if (!%s || %s[0] != 'v' || %s[1] != 'k')\n" \ 98 " return NULL;" % ((name,) * 3) 99 100 def ucc_to_U_C_C(CamelCase): 101 temp = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', CamelCase) 102 return re.sub('([a-z0-9])([A-Z])', r'\1_\2', temp).upper() 103 104 # Parse complete struct chain and add any new ndo_uses to the dict 105 def gather_object_uses_in_struct(obj_list, struct_type): 106 struct_uses = {} 107 if vk_helper.typedef_rev_dict[struct_type] in vk_helper.struct_dict: 108 struct_type = vk_helper.typedef_rev_dict[struct_type] 109 # Parse elements of this struct param to identify objects and/or arrays of objects 110 for m in sorted(vk_helper.struct_dict[struct_type]): 111 array_len = "%s" % (str(vk_helper.struct_dict[struct_type][m]['array_size'])) 112 base_type = vk_helper.struct_dict[struct_type][m]['type'] 113 mem_name = vk_helper.struct_dict[struct_type][m]['name'] 114 if array_len != '0': 115 mem_name = "%s[%s]" % (mem_name, array_len) 116 if base_type in obj_list: 117 #if array_len not in ndo_uses: 118 # struct_uses[array_len] = [] 119 #struct_uses[array_len].append("%s%s,%s" % (name_prefix, struct_name, base_type)) 120 struct_uses[mem_name] = base_type 121 elif vk_helper.is_type(base_type, 'struct'): 122 sub_uses = gather_object_uses_in_struct(obj_list, base_type) 123 if len(sub_uses) > 0: 124 struct_uses[mem_name] = sub_uses 125 return struct_uses 126 127 # For the given list of object types, Parse the given list of params 128 # and return dict of params that use one of the obj_list types 129 # Format of the dict is that terminal elements have <name>,<type> 130 # non-terminal elements will have <name>[<array_size>] 131 # TODO : This analysis could be done up-front at vk_helper time 132 def get_object_uses(obj_list, params): 133 obj_uses = {} 134 local_decls = {} 135 param_count = 'NONE' # track params that give array sizes 136 for p in params: 137 base_type = p.ty.replace('const ', '').strip('*') 138 array_len = '' 139 is_ptr = False 140 if 'count' in p.name.lower(): 141 param_count = p.name 142 ptr_txt = '' 143 if '*' in p.ty: 144 is_ptr = True 145 ptr_txt = '*' 146 if base_type in obj_list: 147 if is_ptr and 'const' in p.ty and param_count != 'NONE': 148 array_len = "[%s]" % param_count 149 # Non-arrays we can overwrite in place, but need local decl for arrays 150 local_decls[p.name] = '%s%s' % (base_type, ptr_txt) 151 #if array_len not in obj_uses: 152 # obj_uses[array_len] = {} 153 # obj_uses[array_len][p.name] = base_type 154 obj_uses["%s%s" % (p.name, array_len)] = base_type 155 elif vk_helper.is_type(base_type, 'struct'): 156 struct_name = p.name 157 if 'NONE' != param_count: 158 struct_name = "%s[%s]" % (struct_name, param_count) 159 struct_uses = gather_object_uses_in_struct(obj_list, base_type) 160 if len(struct_uses) > 0: 161 obj_uses[struct_name] = struct_uses 162 # This is a top-level struct w/ uses below it, so need local decl 163 local_decls['%s' % (p.name)] = '%s%s' % (base_type, ptr_txt) 164 return (obj_uses, local_decls) 165 166 class Subcommand(object): 167 def __init__(self, outfile): 168 self.outfile = outfile 169 self.headers = vulkan.headers 170 self.protos = vulkan.protos 171 self.no_addr = False 172 self.layer_name = "" 173 self.lineinfo = sourcelineinfo() 174 self.wsi = sys.argv[1] 175 176 def run(self): 177 if self.outfile: 178 with open(self.outfile, "w") as outfile: 179 outfile.write(self.generate()) 180 else: 181 print(self.generate()) 182 183 def generate(self): 184 copyright = self.generate_copyright() 185 header = self.generate_header() 186 body = self.generate_body() 187 footer = self.generate_footer() 188 189 contents = [] 190 if copyright: 191 contents.append(copyright) 192 if header: 193 contents.append(header) 194 if body: 195 contents.append(body) 196 if footer: 197 contents.append(footer) 198 199 return "\n\n".join(contents) 200 201 def generate_copyright(self): 202 return """/* THIS FILE IS GENERATED. DO NOT EDIT. */ 203 204 /* 205 * Copyright (c) 2015-2016 The Khronos Group Inc. 206 * Copyright (c) 2015-2016 Valve Corporation 207 * Copyright (c) 2015-2016 LunarG, Inc. 208 * Copyright (c) 2015-2016 Google, Inc. 209 * 210 * Licensed under the Apache License, Version 2.0 (the "License"); 211 * you may not use this file except in compliance with the License. 212 * You may obtain a copy of the License at 213 * 214 * http://www.apache.org/licenses/LICENSE-2.0 215 * 216 * Unless required by applicable law or agreed to in writing, software 217 * distributed under the License is distributed on an "AS IS" BASIS, 218 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 219 * See the License for the specific language governing permissions and 220 * limitations under the License. 221 * 222 * Author: Tobin Ehlis <tobine (at] google.com> 223 * Author: Courtney Goeltzenleuchter <courtneygo (at] google.com> 224 * Author: Jon Ashburn <jon (at] lunarg.com> 225 * Author: Mark Lobodzinski <mark (at] lunarg.com> 226 * Author: Mike Stroyan <stroyan (at] google.com> 227 * Author: Tony Barbour <tony (at] LunarG.com> 228 */""" 229 230 def generate_header(self): 231 return "\n".join(["#include <" + h + ">" for h in self.headers]) 232 233 def generate_body(self): 234 pass 235 236 def generate_footer(self): 237 pass 238 239 # Return set of printf '%' qualifier and input to that qualifier 240 def _get_printf_params(self, vk_type, name, output_param, cpp=False): 241 # TODO : Need ENUM and STRUCT checks here 242 if vk_helper.is_type(vk_type, 'enum'):#"_TYPE" in vk_type: # TODO : This should be generic ENUM check 243 return ("%s", "string_%s(%s)" % (vk_type.replace('const ', '').strip('*'), name)) 244 if "char*" == vk_type: 245 return ("%s", name) 246 if "uint64" in vk_type: 247 if '*' in vk_type: 248 return ("%lu", "*%s" % name) 249 return ("%lu", name) 250 if vk_type.strip('*') in vulkan.object_non_dispatch_list: 251 if '*' in vk_type: 252 return ("%lu", "%s" % name) 253 return ("%lu", "%s" % name) 254 if "size" in vk_type: 255 if '*' in vk_type: 256 return ("%lu", "(unsigned long)*%s" % name) 257 return ("%lu", "(unsigned long)%s" % name) 258 if "float" in vk_type: 259 if '[' in vk_type: # handle array, current hard-coded to 4 (TODO: Make this dynamic) 260 if cpp: 261 return ("[%i, %i, %i, %i]", '"[" << %s[0] << "," << %s[1] << "," << %s[2] << "," << %s[3] << "]"' % (name, name, name, name)) 262 return ("[%f, %f, %f, %f]", "%s[0], %s[1], %s[2], %s[3]" % (name, name, name, name)) 263 return ("%f", name) 264 if "bool" in vk_type.lower() or 'xcb_randr_crtc_t' in vk_type: 265 return ("%u", name) 266 if True in [t in vk_type.lower() for t in ["int", "flags", "mask", "xcb_window_t"]]: 267 if '[' in vk_type: # handle array, current hard-coded to 4 (TODO: Make this dynamic) 268 if cpp: 269 return ("[%i, %i, %i, %i]", "%s[0] << %s[1] << %s[2] << %s[3]" % (name, name, name, name)) 270 return ("[%i, %i, %i, %i]", "%s[0], %s[1], %s[2], %s[3]" % (name, name, name, name)) 271 if '*' in vk_type: 272 if 'pUserData' == name: 273 return ("%i", "((pUserData == 0) ? 0 : *(pUserData))") 274 if 'const' in vk_type.lower(): 275 return ("0x%p", "(void*)(%s)" % name) 276 return ("%i", "*(%s)" % name) 277 return ("%i", name) 278 # TODO : This is special-cased as there's only one "format" param currently and it's nice to expand it 279 if "VkFormat" == vk_type: 280 if cpp: 281 return ("0x%p", "&%s" % name) 282 return ("{%s.channelFormat = %%s, %s.numericFormat = %%s}" % (name, name), "string_VK_COLOR_COMPONENT_FORMAT(%s.channelFormat), string_VK_FORMAT_RANGE_SIZE(%s.numericFormat)" % (name, name)) 283 if output_param: 284 return ("0x%p", "(void*)*%s" % name) 285 if vk_helper.is_type(vk_type, 'struct') and '*' not in vk_type: 286 return ("0x%p", "(void*)(&%s)" % name) 287 return ("0x%p", "(void*)(%s)" % name) 288 289 def _gen_create_msg_callback(self): 290 r_body = [] 291 r_body.append('%s' % self.lineinfo.get()) 292 r_body.append('VKAPI_ATTR VkResult VKAPI_CALL CreateDebugReportCallbackEXT(') 293 r_body.append(' VkInstance instance,') 294 r_body.append(' const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,') 295 r_body.append(' const VkAllocationCallbacks* pAllocator,') 296 r_body.append(' VkDebugReportCallbackEXT* pCallback)') 297 r_body.append('{') 298 # Switch to this code section for the new per-instance storage and debug callbacks 299 if self.layer_name in ['object_tracker', 'unique_objects']: 300 r_body.append(' VkLayerInstanceDispatchTable *pInstanceTable = get_dispatch_table(%s_instance_table_map, instance);' % self.layer_name ) 301 r_body.append(' VkResult result = pInstanceTable->CreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator, pCallback);') 302 r_body.append(' if (VK_SUCCESS == result) {') 303 r_body.append(' layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);') 304 r_body.append(' result = layer_create_msg_callback(my_data->report_data,') 305 r_body.append(' pCreateInfo,') 306 r_body.append(' pAllocator,') 307 r_body.append(' pCallback);') 308 r_body.append(' }') 309 r_body.append(' return result;') 310 else: 311 r_body.append(' VkResult result = instance_dispatch_table(instance)->CreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator, pCallback);') 312 r_body.append(' if (VK_SUCCESS == result) {') 313 r_body.append(' layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);') 314 r_body.append(' result = layer_create_msg_callback(my_data->report_data, pCreateInfo, pAllocator, pCallback);') 315 r_body.append(' }') 316 r_body.append(' return result;') 317 r_body.append('}') 318 return "\n".join(r_body) 319 320 def _gen_destroy_msg_callback(self): 321 r_body = [] 322 r_body.append('%s' % self.lineinfo.get()) 323 r_body.append('VKAPI_ATTR void VKAPI_CALL DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT msgCallback, const VkAllocationCallbacks *pAllocator)') 324 r_body.append('{') 325 # Switch to this code section for the new per-instance storage and debug callbacks 326 if self.layer_name in ['object_tracker', 'unique_objects']: 327 r_body.append(' VkLayerInstanceDispatchTable *pInstanceTable = get_dispatch_table(%s_instance_table_map, instance);' % self.layer_name ) 328 else: 329 r_body.append(' VkLayerInstanceDispatchTable *pInstanceTable = instance_dispatch_table(instance);') 330 r_body.append(' pInstanceTable->DestroyDebugReportCallbackEXT(instance, msgCallback, pAllocator);') 331 r_body.append(' layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);') 332 r_body.append(' layer_destroy_msg_callback(my_data->report_data, msgCallback, pAllocator);') 333 r_body.append('}') 334 return "\n".join(r_body) 335 336 def _gen_debug_report_msg(self): 337 r_body = [] 338 r_body.append('%s' % self.lineinfo.get()) 339 r_body.append('VKAPI_ATTR void VKAPI_CALL DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg)') 340 r_body.append('{') 341 # Switch to this code section for the new per-instance storage and debug callbacks 342 if self.layer_name == 'object_tracker': 343 r_body.append(' VkLayerInstanceDispatchTable *pInstanceTable = get_dispatch_table(%s_instance_table_map, instance);' % self.layer_name ) 344 else: 345 r_body.append(' VkLayerInstanceDispatchTable *pInstanceTable = instance_dispatch_table(instance);') 346 r_body.append(' pInstanceTable->DebugReportMessageEXT(instance, flags, objType, object, location, msgCode, pLayerPrefix, pMsg);') 347 r_body.append('}') 348 return "\n".join(r_body) 349 350 def _gen_layer_logging_workaround(self): 351 body = [] 352 body.append('%s' % self.lineinfo.get()) 353 body.append('// vk_layer_logging.h expects these to be defined') 354 body.append('') 355 body.append('VKAPI_ATTR VkResult VKAPI_CALL') 356 body.append('vkCreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,') 357 body.append(' const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pMsgCallback) {') 358 body.append(' return %s::CreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator, pMsgCallback);' % self.layer_name) 359 body.append('}') 360 body.append('') 361 body.append('VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT(VkInstance instance,') 362 body.append(' VkDebugReportCallbackEXT msgCallback,') 363 body.append(' const VkAllocationCallbacks *pAllocator) {') 364 body.append(' %s::DestroyDebugReportCallbackEXT(instance, msgCallback, pAllocator);' % self.layer_name) 365 body.append('}') 366 body.append('') 367 body.append('VKAPI_ATTR void VKAPI_CALL') 368 body.append('vkDebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t object,') 369 body.append(' size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg) {') 370 body.append(' %s::DebugReportMessageEXT(instance, flags, objType, object, location, msgCode, pLayerPrefix, pMsg);' % self.layer_name) 371 body.append('}') 372 373 return "\n".join(body) 374 375 def _gen_layer_interface_v0_functions(self): 376 body = [] 377 body.append('%s' % self.lineinfo.get()) 378 body.append('// loader-layer interface v0') 379 body.append('') 380 381 if self.layer_name == 'object_tracker': 382 body.append('static const VkExtensionProperties instance_extensions[] = {') 383 body.append(' {') 384 body.append(' VK_EXT_DEBUG_REPORT_EXTENSION_NAME,') 385 body.append(' VK_EXT_DEBUG_REPORT_SPEC_VERSION') 386 body.append(' }') 387 body.append('};') 388 body.append('') 389 390 body.append('static const VkLayerProperties globalLayerProps = {') 391 body.append(' "VK_LAYER_LUNARG_%s",' % self.layer_name) 392 body.append(' VK_LAYER_API_VERSION, // specVersion') 393 body.append(' 1, // implementationVersion') 394 body.append(' "LunarG Validation Layer"') 395 body.append('};') 396 body.append('') 397 else: 398 body.append('static const VkLayerProperties globalLayerProps = {') 399 body.append(' "VK_LAYER_GOOGLE_%s",' % self.layer_name) 400 body.append(' VK_LAYER_API_VERSION, // specVersion') 401 body.append(' 1, // implementationVersion') 402 body.append(' "Google Validation Layer"') 403 body.append('};') 404 body.append('') 405 406 body.append('VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pCount, VkExtensionProperties* pProperties)') 407 body.append('{') 408 if self.layer_name == 'object_tracker': 409 body.append(' return util_GetExtensionProperties(1, instance_extensions, pCount, pProperties);') 410 else: 411 body.append(' return util_GetExtensionProperties(0, NULL, pCount, pProperties);') 412 body.append('}') 413 body.append('') 414 body.append('VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties* pProperties)') 415 body.append('{') 416 body.append(' return util_GetLayerProperties(1, &globalLayerProps, pCount, pProperties);') 417 body.append('}') 418 body.append('') 419 body.append('VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pCount, VkLayerProperties* pProperties)') 420 body.append('{') 421 body.append(' return util_GetLayerProperties(1, &globalLayerProps, pCount, pProperties);') 422 body.append('}') 423 body.append('') 424 body.append('VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName)') 425 body.append('{') 426 body.append(' return %s::GetDeviceProcAddr(dev, funcName);' % self.layer_name) 427 body.append('}') 428 body.append('') 429 body.append('VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName)') 430 body.append('{') 431 body.append(' if (!strcmp(funcName, "vkEnumerateInstanceLayerProperties"))') 432 body.append(' return reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateInstanceLayerProperties);') 433 body.append(' if (!strcmp(funcName, "vkEnumerateDeviceLayerProperties"))') 434 body.append(' return reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateDeviceLayerProperties);') 435 body.append(' if (!strcmp(funcName, "vkEnumerateInstanceExtensionProperties"))') 436 body.append(' return reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateInstanceExtensionProperties);') 437 body.append(' if (!strcmp(funcName, "vkGetInstanceProcAddr"))') 438 body.append(' return reinterpret_cast<PFN_vkVoidFunction>(vkGetInstanceProcAddr);') 439 body.append('') 440 body.append(' return %s::GetInstanceProcAddr(instance, funcName);' % self.layer_name) 441 body.append('}') 442 443 return "\n".join(body) 444 445 def _generate_dispatch_entrypoints(self, qual=""): 446 if qual: 447 qual += " " 448 449 funcs = [] 450 intercepted = [] 451 for proto in self.protos: 452 if proto.name in ["EnumerateInstanceExtensionProperties", 453 "EnumerateInstanceLayerProperties", 454 "EnumerateDeviceLayerProperties"]: 455 # the layer do not need to define these 456 continue 457 elif proto.name in ["GetDeviceProcAddr", 458 "GetInstanceProcAddr"]: 459 funcs.append(proto.c_func(attr="VKAPI") + ';') 460 intercepted.append(proto) 461 else: 462 intercept = self.generate_intercept(proto, qual) 463 if intercept is None: 464 # fill in default intercept for certain entrypoints 465 if 'CreateDebugReportCallbackEXT' == proto.name: 466 intercept = self._gen_layer_dbg_create_msg_callback() 467 elif 'DestroyDebugReportCallbackEXT' == proto.name: 468 intercept = self._gen_layer_dbg_destroy_msg_callback() 469 elif 'DebugReportMessageEXT' == proto.name: 470 intercept = self._gen_debug_report_msg() 471 elif 'CreateDevice' == proto.name: 472 funcs.append('/* CreateDevice HERE */') 473 474 if intercept is not None: 475 funcs.append(intercept) 476 if not "KHR" in proto.name: 477 intercepted.append(proto) 478 479 instance_lookups = [] 480 device_lookups = [] 481 for proto in intercepted: 482 if proto_is_global(proto): 483 instance_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name) 484 instance_lookups.append(" return (PFN_vkVoidFunction) %s;" % (proto.name)) 485 else: 486 device_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name) 487 device_lookups.append(" return (PFN_vkVoidFunction) %s;" % (proto.name)) 488 489 # add customized intercept_core_device_command 490 body = [] 491 body.append('%s' % self.lineinfo.get()) 492 body.append("static inline PFN_vkVoidFunction intercept_core_device_command(const char *name)") 493 body.append("{") 494 body.append(generate_get_proc_addr_check("name")) 495 body.append("") 496 body.append(" name += 2;") 497 body.append(" %s" % "\n ".join(device_lookups)) 498 body.append("") 499 body.append(" return NULL;") 500 body.append("}") 501 # add intercept_core_instance_command 502 body.append("static inline PFN_vkVoidFunction intercept_core_instance_command(const char *name)") 503 body.append("{") 504 body.append(generate_get_proc_addr_check("name")) 505 body.append("") 506 body.append(" // we should never be queried for these commands") 507 body.append(" assert(strcmp(name, \"vkEnumerateInstanceLayerProperties\") &&") 508 body.append(" strcmp(name, \"vkEnumerateInstanceExtensionProperties\") &&") 509 body.append(" strcmp(name, \"vkEnumerateDeviceLayerProperties\"));") 510 body.append("") 511 body.append(" name += 2;") 512 body.append(" %s" % "\n ".join(instance_lookups)) 513 body.append("") 514 body.append(" return NULL;") 515 body.append("}") 516 517 funcs.append("\n".join(body)) 518 return "\n\n".join(funcs) 519 520 def _generate_extensions(self): 521 exts = [] 522 exts.append('%s' % self.lineinfo.get()) 523 exts.append(self._gen_create_msg_callback()) 524 exts.append(self._gen_destroy_msg_callback()) 525 exts.append(self._gen_debug_report_msg()) 526 return "\n".join(exts) 527 528 def _generate_layer_gpa_function(self, extensions=[], instance_extensions=[]): 529 func_body = [] 530 # 531 # New style of GPA Functions for the new layer_data/layer_logging changes 532 # 533 if self.layer_name in ['object_tracker', 'unique_objects']: 534 for ext_enable, ext_list in extensions: 535 func_body.append('%s' % self.lineinfo.get()) 536 func_body.append('static inline PFN_vkVoidFunction intercept_%s_command(const char *name, VkDevice dev)' % ext_enable) 537 func_body.append('{') 538 func_body.append(' if (dev) {') 539 func_body.append(' layer_data *my_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map);') 540 func_body.append(' if (!my_data->%s)' % ext_enable) 541 func_body.append(' return nullptr;') 542 func_body.append(' }\n') 543 544 for ext_name in ext_list: 545 func_body.append(' if (!strcmp("%s", name))\n' 546 ' return reinterpret_cast<PFN_vkVoidFunction>(%s);' % (ext_name, ext_name[2:])) 547 func_body.append('\n return nullptr;') 548 func_body.append('}\n') 549 550 func_body.append("VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char* funcName)\n" 551 "{\n" 552 " PFN_vkVoidFunction addr;\n" 553 " addr = intercept_core_device_command(funcName);\n" 554 " if (addr)\n" 555 " return addr;\n" 556 " assert(device);\n") 557 for ext_enable, _ in extensions: 558 func_body.append(' addr = intercept_%s_command(funcName, device);' % ext_enable) 559 func_body.append(' if (addr)\n' 560 ' return addr;') 561 func_body.append("\n if (get_dispatch_table(%s_device_table_map, device)->GetDeviceProcAddr == NULL)\n" 562 " return NULL;\n" 563 " return get_dispatch_table(%s_device_table_map, device)->GetDeviceProcAddr(device, funcName);\n" 564 "}\n" % (self.layer_name, self.layer_name)) 565 566 for ext_enable, ext_list in instance_extensions: 567 func_body.append('%s' % self.lineinfo.get()) 568 func_body.append('static inline PFN_vkVoidFunction intercept_%s_command(const char *name, VkInstance instance)' % ext_enable) 569 func_body.append('{') 570 if ext_enable == 'msg_callback_get_proc_addr': 571 func_body.append(" layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);\n" 572 " return debug_report_get_instance_proc_addr(my_data->report_data, name);") 573 else: 574 func_body.append(" VkLayerInstanceDispatchTable* pTable = get_dispatch_table(%s_instance_table_map, instance);" % self.layer_name) 575 func_body.append(' if (instanceExtMap.size() == 0 || !instanceExtMap[pTable].%s)' % ext_enable) 576 func_body.append(' return nullptr;\n') 577 578 for ext_name in ext_list: 579 if wsi_name(ext_name): 580 func_body.append('%s' % wsi_ifdef(ext_name)) 581 func_body.append(' if (!strcmp("%s", name))\n' 582 ' return reinterpret_cast<PFN_vkVoidFunction>(%s);' % (ext_name, ext_name[2:])) 583 if wsi_name(ext_name): 584 func_body.append('%s' % wsi_endif(ext_name)) 585 586 func_body.append('\n return nullptr;') 587 func_body.append('}\n') 588 589 func_body.append("VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName)\n" 590 "{\n" 591 " PFN_vkVoidFunction addr;\n" 592 " addr = intercept_core_instance_command(funcName);\n" 593 " if (!addr) {\n" 594 " addr = intercept_core_device_command(funcName);\n" 595 " }") 596 597 for ext_enable, _ in extensions: 598 func_body.append(" if (!addr) {\n" 599 " addr = intercept_%s_command(funcName, VkDevice(VK_NULL_HANDLE));\n" 600 " }" % ext_enable) 601 602 func_body.append(" if (addr) {\n" 603 " return addr;\n" 604 " }\n" 605 " assert(instance);\n" 606 ) 607 608 for ext_enable, _ in instance_extensions: 609 func_body.append(' addr = intercept_%s_command(funcName, instance);' % ext_enable) 610 func_body.append(' if (addr)\n' 611 ' return addr;\n') 612 613 func_body.append(" if (get_dispatch_table(%s_instance_table_map, instance)->GetInstanceProcAddr == NULL) {\n" 614 " return NULL;\n" 615 " }\n" 616 " return get_dispatch_table(%s_instance_table_map, instance)->GetInstanceProcAddr(instance, funcName);\n" 617 "}\n" % (self.layer_name, self.layer_name)) 618 return "\n".join(func_body) 619 else: 620 func_body.append('%s' % self.lineinfo.get()) 621 func_body.append("VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char* funcName)\n" 622 "{\n" 623 " PFN_vkVoidFunction addr;\n") 624 func_body.append("\n" 625 " loader_platform_thread_once(&initOnce, init%s);\n\n" 626 " addr = intercept_core_device_command(funcName);\n" 627 " if (addr)\n" 628 " return addr;" % self.layer_name) 629 func_body.append(" assert(device);\n") 630 func_body.append('') 631 func_body.append(' VkLayerDispatchTable *pDisp = device_dispatch_table(device);') 632 if 0 != len(extensions): 633 extra_space = "" 634 for (ext_enable, ext_list) in extensions: 635 if 0 != len(ext_enable): 636 func_body.append(' if (deviceExtMap.size() != 0 && deviceExtMap[pDisp].%s)' % ext_enable) 637 func_body.append(' {') 638 extra_space = " " 639 for ext_name in ext_list: 640 func_body.append(' %sif (!strcmp("%s", funcName))\n' 641 ' return reinterpret_cast<PFN_vkVoidFunction>(%s);' % (extra_space, ext_name, ext_name)) 642 if 0 != len(ext_enable): 643 func_body.append(' }') 644 func_body.append('%s' % self.lineinfo.get()) 645 func_body.append(" {\n" 646 " if (pDisp->GetDeviceProcAddr == NULL)\n" 647 " return NULL;\n" 648 " return pDisp->GetDeviceProcAddr(device, funcName);\n" 649 " }\n" 650 "}\n") 651 func_body.append('%s' % self.lineinfo.get()) 652 func_body.append("VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName)\n" 653 "{\n" 654 " PFN_vkVoidFunction addr;\n" 655 ) 656 func_body.append( 657 " loader_platform_thread_once(&initOnce, init%s);\n\n" 658 " addr = intercept_core_instance_command(funcName);\n" 659 " if (addr)\n" 660 " return addr;" % self.layer_name) 661 func_body.append(" assert(instance);\n") 662 func_body.append("") 663 func_body.append(" VkLayerInstanceDispatchTable* pTable = instance_dispatch_table(instance);\n") 664 if 0 != len(instance_extensions): 665 extra_space = "" 666 for (ext_enable, ext_list) in instance_extensions: 667 if 0 != len(ext_enable): 668 if ext_enable == 'msg_callback_get_proc_addr': 669 func_body.append(" layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);\n" 670 " addr = debug_report_get_instance_proc_addr(my_data->report_data, funcName);\n" 671 " if (addr) {\n" 672 " return addr;\n" 673 " }\n") 674 else: 675 func_body.append(' if (instanceExtMap.size() != 0 && instanceExtMap[pTable].%s)' % ext_enable) 676 func_body.append(' {') 677 extra_space = " " 678 for ext_name in ext_list: 679 if wsi_name(ext_name): 680 func_body.append('%s' % wsi_ifdef(ext_name)) 681 func_body.append(' %sif (!strcmp("%s", funcName))\n' 682 ' return reinterpret_cast<PFN_vkVoidFunction>(%s);' % (extra_space, ext_name, ext_name)) 683 if wsi_name(ext_name): 684 func_body.append('%s' % wsi_endif(ext_name)) 685 if 0 != len(ext_enable): 686 func_body.append(' }\n') 687 688 func_body.append(" if (pTable->GetInstanceProcAddr == NULL)\n" 689 " return NULL;\n" 690 " return pTable->GetInstanceProcAddr(instance, funcName);\n" 691 "}\n") 692 return "\n".join(func_body) 693 694 695 def _generate_layer_initialization(self, init_opts=False, prefix='vk', lockname=None, condname=None): 696 func_body = ["#include \"vk_dispatch_table_helper.h\""] 697 func_body.append('%s' % self.lineinfo.get()) 698 func_body.append('static void init_%s(layer_data *my_data, const VkAllocationCallbacks *pAllocator)\n' 699 '{\n' % self.layer_name) 700 if init_opts: 701 func_body.append('%s' % self.lineinfo.get()) 702 func_body.append('') 703 func_body.append(' layer_debug_actions(my_data->report_data, my_data->logging_callback, pAllocator, "lunarg_%s");' % self.layer_name) 704 func_body.append('') 705 if lockname is not None: 706 func_body.append('%s' % self.lineinfo.get()) 707 func_body.append(" if (!%sLockInitialized)" % lockname) 708 func_body.append(" {") 709 func_body.append(" // TODO/TBD: Need to delete this mutex sometime. How???") 710 func_body.append(" loader_platform_thread_create_mutex(&%sLock);" % lockname) 711 if condname is not None: 712 func_body.append(" loader_platform_thread_init_cond(&%sCond);" % condname) 713 func_body.append(" %sLockInitialized = 1;" % lockname) 714 func_body.append(" }") 715 func_body.append("}\n") 716 func_body.append('') 717 return "\n".join(func_body) 718 719 class ObjectTrackerSubcommand(Subcommand): 720 def generate_header(self): 721 header_txt = [] 722 header_txt.append('%s' % self.lineinfo.get()) 723 header_txt.append('#include "vk_loader_platform.h"') 724 header_txt.append('#include "vulkan/vulkan.h"') 725 header_txt.append('') 726 header_txt.append('#include <stdio.h>') 727 header_txt.append('#include <stdlib.h>') 728 header_txt.append('#include <string.h>') 729 header_txt.append('#include <cinttypes>') 730 header_txt.append('') 731 header_txt.append('#include <unordered_map>') 732 header_txt.append('') 733 header_txt.append('#include "vulkan/vk_layer.h"') 734 header_txt.append('#include "vk_layer_config.h"') 735 header_txt.append('#include "vk_layer_table.h"') 736 header_txt.append('#include "vk_layer_data.h"') 737 header_txt.append('#include "vk_layer_logging.h"') 738 header_txt.append('') 739 # NOTE: The non-autoGenerated code is in the object_tracker.h header file 740 header_txt.append('#include "object_tracker.h"') 741 header_txt.append('') 742 return "\n".join(header_txt) 743 744 def generate_maps(self): 745 maps_txt = [] 746 for o in vulkan.object_type_list: 747 maps_txt.append('std::unordered_map<uint64_t, OBJTRACK_NODE*> %sMap;' % (o)) 748 return "\n".join(maps_txt) 749 750 def _gather_object_uses(self, obj_list, struct_type, obj_set): 751 # for each member of struct_type 752 # add objs in obj_list to obj_set 753 # call self for structs 754 for m in sorted(vk_helper.struct_dict[struct_type]): 755 if vk_helper.struct_dict[struct_type][m]['type'] in obj_list: 756 obj_set.add(vk_helper.struct_dict[struct_type][m]['type']) 757 elif vk_helper.is_type(vk_helper.struct_dict[struct_type][m]['type'], 'struct'): 758 obj_set = obj_set.union(self._gather_object_uses(obj_list, vk_helper.struct_dict[struct_type][m]['type'], obj_set)) 759 return obj_set 760 761 def generate_procs(self): 762 procs_txt = [] 763 # First parse through funcs and gather dict of all objects seen by each call 764 obj_use_dict = {} 765 proto_list = vulkan.core.protos + vulkan.ext_khr_surface.protos + vulkan.ext_khr_surface.protos + vulkan.ext_khr_win32_surface.protos + vulkan.ext_khr_device_swapchain.protos 766 for proto in proto_list: 767 disp_obj = proto.params[0].ty.strip('*').replace('const ', '') 768 if disp_obj in vulkan.object_dispatch_list: 769 if disp_obj not in obj_use_dict: 770 obj_use_dict[disp_obj] = set() 771 for p in proto.params[1:]: 772 base_type = p.ty.strip('*').replace('const ', '') 773 if base_type in vulkan.object_type_list: 774 obj_use_dict[disp_obj].add(base_type) 775 if vk_helper.is_type(base_type, 'struct'): 776 obj_use_dict[disp_obj] = self._gather_object_uses(vulkan.object_type_list, base_type, obj_use_dict[disp_obj]) 777 #for do in obj_use_dict: 778 # print "Disp obj %s has uses for objs: %s" % (do, ', '.join(obj_use_dict[do])) 779 780 for o in vulkan.object_type_list:# vulkan.core.objects: 781 procs_txt.append('%s' % self.lineinfo.get()) 782 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', o) 783 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 784 if o in vulkan.object_dispatch_list: 785 procs_txt.append('static void create_%s(%s dispatchable_object, %s vkObj, VkDebugReportObjectTypeEXT objType)' % (name, o, o)) 786 else: 787 procs_txt.append('static void create_%s(VkDevice dispatchable_object, %s vkObj, VkDebugReportObjectTypeEXT objType)' % (name, o)) 788 procs_txt.append('{') 789 procs_txt.append(' log_msg(mdd(dispatchable_object), VK_DEBUG_REPORT_INFORMATION_BIT_EXT, objType,(uint64_t)(vkObj), __LINE__, OBJTRACK_NONE, "OBJTRACK",') 790 procs_txt.append(' "OBJ[%llu] : CREATE %s object 0x%" PRIxLEAST64 , object_track_index++, string_VkDebugReportObjectTypeEXT(objType),') 791 procs_txt.append(' (uint64_t)(vkObj));') 792 procs_txt.append('') 793 procs_txt.append(' OBJTRACK_NODE* pNewObjNode = new OBJTRACK_NODE;') 794 procs_txt.append(' pNewObjNode->belongsTo = (uint64_t)dispatchable_object;') 795 procs_txt.append(' pNewObjNode->objType = objType;') 796 procs_txt.append(' pNewObjNode->status = OBJSTATUS_NONE;') 797 procs_txt.append(' pNewObjNode->vkObj = (uint64_t)(vkObj);') 798 procs_txt.append(' %sMap[(uint64_t)vkObj] = pNewObjNode;' % (o)) 799 procs_txt.append(' uint32_t objIndex = objTypeToIndex(objType);') 800 procs_txt.append(' numObjs[objIndex]++;') 801 procs_txt.append(' numTotalObjs++;') 802 procs_txt.append('}') 803 procs_txt.append('') 804 procs_txt.append('%s' % self.lineinfo.get()) 805 if o in vulkan.object_dispatch_list: 806 procs_txt.append('static void destroy_%s(%s dispatchable_object, %s object)' % (name, o, o)) 807 else: 808 procs_txt.append('static void destroy_%s(VkDevice dispatchable_object, %s object)' % (name, o)) 809 procs_txt.append('{') 810 procs_txt.append(' uint64_t object_handle = (uint64_t)(object);') 811 procs_txt.append(' auto it = %sMap.find(object_handle);' % o) 812 procs_txt.append(' if (it != %sMap.end()) {' % o) 813 procs_txt.append(' OBJTRACK_NODE* pNode = it->second;') 814 procs_txt.append(' uint32_t objIndex = objTypeToIndex(pNode->objType);') 815 procs_txt.append(' assert(numTotalObjs > 0);') 816 procs_txt.append(' numTotalObjs--;') 817 procs_txt.append(' assert(numObjs[objIndex] > 0);') 818 procs_txt.append(' numObjs[objIndex]--;') 819 procs_txt.append(' log_msg(mdd(dispatchable_object), VK_DEBUG_REPORT_INFORMATION_BIT_EXT, pNode->objType, object_handle, __LINE__, OBJTRACK_NONE, "OBJTRACK",') 820 procs_txt.append(' "OBJ_STAT Destroy %s obj 0x%" PRIxLEAST64 " (%" PRIu64 " total objs remain & %" PRIu64 " %s objs).",') 821 procs_txt.append(' string_VkDebugReportObjectTypeEXT(pNode->objType), (uint64_t)(object), numTotalObjs, numObjs[objIndex],') 822 procs_txt.append(' string_VkDebugReportObjectTypeEXT(pNode->objType));') 823 procs_txt.append(' delete pNode;') 824 procs_txt.append(' %sMap.erase(it);' % (o)) 825 procs_txt.append(' } else {') 826 procs_txt.append(' log_msg(mdd(dispatchable_object), VK_DEBUG_REPORT_ERROR_BIT_EXT, (VkDebugReportObjectTypeEXT ) 0,') 827 procs_txt.append(' object_handle, __LINE__, OBJTRACK_UNKNOWN_OBJECT, "OBJTRACK",') 828 procs_txt.append(' "Unable to remove obj 0x%" PRIxLEAST64 ". Was it created? Has it already been destroyed?",') 829 procs_txt.append(' object_handle);') 830 procs_txt.append(' }') 831 procs_txt.append('}') 832 procs_txt.append('') 833 # Generate the permutations of validate_* functions where for each 834 # dispatchable object type, we have a corresponding validate_* function 835 # for that object and all non-dispatchable objects that are used in API 836 # calls with that dispatchable object. 837 procs_txt.append('//%s' % str(sorted(obj_use_dict))) 838 for do in sorted(obj_use_dict): 839 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', do) 840 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 841 # First create validate_* func for disp obj 842 procs_txt.append('%s' % self.lineinfo.get()) 843 procs_txt.append('static bool validate_%s(%s dispatchable_object, %s object, VkDebugReportObjectTypeEXT objType, bool null_allowed)' % (name, do, do)) 844 procs_txt.append('{') 845 procs_txt.append(' if (null_allowed && (object == VK_NULL_HANDLE))') 846 procs_txt.append(' return false;') 847 procs_txt.append(' if (%sMap.find((uint64_t)object) == %sMap.end()) {' % (do, do)) 848 procs_txt.append(' return log_msg(mdd(dispatchable_object), VK_DEBUG_REPORT_ERROR_BIT_EXT, objType, (uint64_t)(object), __LINE__, OBJTRACK_INVALID_OBJECT, "OBJTRACK",') 849 procs_txt.append(' "Invalid %s Object 0x%%" PRIx64 ,(uint64_t)(object));' % do) 850 procs_txt.append(' }') 851 procs_txt.append(' return false;') 852 procs_txt.append('}') 853 procs_txt.append('') 854 for o in sorted(obj_use_dict[do]): 855 if o == do: # We already generated this case above so skip here 856 continue 857 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', o) 858 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 859 procs_txt.append('%s' % self.lineinfo.get()) 860 procs_txt.append('static bool validate_%s(%s dispatchable_object, %s object, VkDebugReportObjectTypeEXT objType, bool null_allowed)' % (name, do, o)) 861 procs_txt.append('{') 862 procs_txt.append(' if (null_allowed && (object == VK_NULL_HANDLE))') 863 procs_txt.append(' return false;') 864 if o == "VkImage": 865 procs_txt.append(' // We need to validate normal image objects and those from the swapchain') 866 procs_txt.append(' if ((%sMap.find((uint64_t)object) == %sMap.end()) &&' % (o, o)) 867 procs_txt.append(' (swapchainImageMap.find((uint64_t)object) == swapchainImageMap.end())) {') 868 else: 869 procs_txt.append(' if (%sMap.find((uint64_t)object) == %sMap.end()) {' % (o, o)) 870 procs_txt.append(' return log_msg(mdd(dispatchable_object), VK_DEBUG_REPORT_ERROR_BIT_EXT, objType, (uint64_t)(object), __LINE__, OBJTRACK_INVALID_OBJECT, "OBJTRACK",') 871 procs_txt.append(' "Invalid %s Object 0x%%" PRIx64, (uint64_t)(object));' % o) 872 procs_txt.append(' }') 873 procs_txt.append(' return false;') 874 procs_txt.append('}') 875 procs_txt.append('') 876 procs_txt.append('') 877 return "\n".join(procs_txt) 878 879 def generate_destroy_instance(self): 880 gedi_txt = [] 881 gedi_txt.append('%s' % self.lineinfo.get()) 882 gedi_txt.append('VKAPI_ATTR void VKAPI_CALL DestroyInstance(') 883 gedi_txt.append('VkInstance instance,') 884 gedi_txt.append('const VkAllocationCallbacks* pAllocator)') 885 gedi_txt.append('{') 886 gedi_txt.append(' std::unique_lock<std::mutex> lock(global_lock);') 887 gedi_txt.append('') 888 gedi_txt.append(' dispatch_key key = get_dispatch_key(instance);') 889 gedi_txt.append(' layer_data *my_data = get_my_data_ptr(key, layer_data_map);') 890 gedi_txt.append('') 891 gedi_txt.append(' // Enable the temporary callback(s) here to catch cleanup issues:') 892 gedi_txt.append(' bool callback_setup = false;') 893 gedi_txt.append(' if (my_data->num_tmp_callbacks > 0) {') 894 gedi_txt.append(' if (!layer_enable_tmp_callbacks(my_data->report_data,') 895 gedi_txt.append(' my_data->num_tmp_callbacks,') 896 gedi_txt.append(' my_data->tmp_dbg_create_infos,') 897 gedi_txt.append(' my_data->tmp_callbacks)) {') 898 gedi_txt.append(' callback_setup = true;') 899 gedi_txt.append(' }') 900 gedi_txt.append(' }') 901 gedi_txt.append('') 902 gedi_txt.append(' validate_instance(instance, instance, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, false);') 903 gedi_txt.append('') 904 gedi_txt.append(' destroy_instance(instance, instance);') 905 gedi_txt.append(' // Report any remaining objects in LL') 906 gedi_txt.append('') 907 gedi_txt.append(' for (auto iit = VkDeviceMap.begin(); iit != VkDeviceMap.end();) {') 908 gedi_txt.append(' OBJTRACK_NODE* pNode = iit->second;') 909 gedi_txt.append(' if (pNode->belongsTo == (uint64_t)instance) {') 910 gedi_txt.append(' log_msg(mid(instance), VK_DEBUG_REPORT_ERROR_BIT_EXT, pNode->objType, pNode->vkObj, __LINE__, OBJTRACK_OBJECT_LEAK, "OBJTRACK",') 911 gedi_txt.append(' "OBJ ERROR : %s object 0x%" PRIxLEAST64 " has not been destroyed.", string_VkDebugReportObjectTypeEXT(pNode->objType),') 912 gedi_txt.append(' pNode->vkObj);') 913 for o in vulkan.core.objects: 914 if o in ['VkInstance', 'VkPhysicalDevice', 'VkQueue', 'VkDevice']: 915 continue 916 gedi_txt.append(' for (auto idt = %sMap.begin(); idt != %sMap.end();) {' % (o, o)) 917 gedi_txt.append(' OBJTRACK_NODE* pNode = idt->second;') 918 gedi_txt.append(' if (pNode->belongsTo == iit->first) {') 919 gedi_txt.append(' log_msg(mid(instance), VK_DEBUG_REPORT_ERROR_BIT_EXT, pNode->objType, pNode->vkObj, __LINE__, OBJTRACK_OBJECT_LEAK, "OBJTRACK",') 920 gedi_txt.append(' "OBJ ERROR : %s object 0x%" PRIxLEAST64 " has not been destroyed.", string_VkDebugReportObjectTypeEXT(pNode->objType),') 921 gedi_txt.append(' pNode->vkObj);') 922 gedi_txt.append(' %sMap.erase(idt++);' % o ) 923 gedi_txt.append(' } else {') 924 gedi_txt.append(' ++idt;') 925 gedi_txt.append(' }') 926 gedi_txt.append(' }') 927 gedi_txt.append(' VkDeviceMap.erase(iit++);') 928 gedi_txt.append(' } else {') 929 gedi_txt.append(' ++iit;') 930 gedi_txt.append(' }') 931 gedi_txt.append(' }') 932 gedi_txt.append('') 933 gedi_txt.append(' VkLayerInstanceDispatchTable *pInstanceTable = get_dispatch_table(object_tracker_instance_table_map, instance);') 934 gedi_txt.append(' pInstanceTable->DestroyInstance(instance, pAllocator);') 935 gedi_txt.append('') 936 gedi_txt.append(' // Disable and cleanup the temporary callback(s):') 937 gedi_txt.append(' if (callback_setup) {') 938 gedi_txt.append(' layer_disable_tmp_callbacks(my_data->report_data,') 939 gedi_txt.append(' my_data->num_tmp_callbacks,') 940 gedi_txt.append(' my_data->tmp_callbacks);') 941 gedi_txt.append(' }') 942 gedi_txt.append(' if (my_data->num_tmp_callbacks > 0) {') 943 gedi_txt.append(' layer_free_tmp_callbacks(my_data->tmp_dbg_create_infos,') 944 gedi_txt.append(' my_data->tmp_callbacks);') 945 gedi_txt.append(' my_data->num_tmp_callbacks = 0;') 946 gedi_txt.append(' }') 947 gedi_txt.append('') 948 gedi_txt.append(' // Clean up logging callback, if any') 949 gedi_txt.append(' while (my_data->logging_callback.size() > 0) {') 950 gedi_txt.append(' VkDebugReportCallbackEXT callback = my_data->logging_callback.back();') 951 gedi_txt.append(' layer_destroy_msg_callback(my_data->report_data, callback, pAllocator);') 952 gedi_txt.append(' my_data->logging_callback.pop_back();') 953 gedi_txt.append(' }') 954 gedi_txt.append('') 955 gedi_txt.append(' layer_debug_report_destroy_instance(mid(instance));') 956 gedi_txt.append(' layer_data_map.erase(key);') 957 gedi_txt.append('') 958 gedi_txt.append(' instanceExtMap.erase(pInstanceTable);') 959 gedi_txt.append(' lock.unlock();') 960 # The loader holds a mutex that protects this from other threads 961 gedi_txt.append(' object_tracker_instance_table_map.erase(key);') 962 gedi_txt.append('}') 963 gedi_txt.append('') 964 return "\n".join(gedi_txt) 965 966 def generate_destroy_device(self): 967 gedd_txt = [] 968 gedd_txt.append('%s' % self.lineinfo.get()) 969 gedd_txt.append('VKAPI_ATTR void VKAPI_CALL DestroyDevice(') 970 gedd_txt.append('VkDevice device,') 971 gedd_txt.append('const VkAllocationCallbacks* pAllocator)') 972 gedd_txt.append('{') 973 gedd_txt.append(' std::unique_lock<std::mutex> lock(global_lock);') 974 gedd_txt.append(' validate_device(device, device, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, false);') 975 gedd_txt.append('') 976 gedd_txt.append(' destroy_device(device, device);') 977 gedd_txt.append(' // Report any remaining objects associated with this VkDevice object in LL') 978 for o in vulkan.core.objects: 979 # DescriptorSets and Command Buffers are destroyed through their pools, not explicitly 980 if o in ['VkInstance', 'VkPhysicalDevice', 'VkQueue', 'VkDevice', 'VkDescriptorSet', 'VkCommandBuffer']: 981 continue 982 gedd_txt.append(' for (auto it = %sMap.begin(); it != %sMap.end();) {' % (o, o)) 983 gedd_txt.append(' OBJTRACK_NODE* pNode = it->second;') 984 gedd_txt.append(' if (pNode->belongsTo == (uint64_t)device) {') 985 gedd_txt.append(' log_msg(mdd(device), VK_DEBUG_REPORT_ERROR_BIT_EXT, pNode->objType, pNode->vkObj, __LINE__, OBJTRACK_OBJECT_LEAK, "OBJTRACK",') 986 gedd_txt.append(' "OBJ ERROR : %s object 0x%" PRIxLEAST64 " has not been destroyed.", string_VkDebugReportObjectTypeEXT(pNode->objType),') 987 gedd_txt.append(' pNode->vkObj);') 988 gedd_txt.append(' %sMap.erase(it++);' % o ) 989 gedd_txt.append(' } else {') 990 gedd_txt.append(' ++it;') 991 gedd_txt.append(' }') 992 gedd_txt.append(' }') 993 gedd_txt.append('') 994 gedd_txt.append(" // Clean up Queue's MemRef Linked Lists") 995 gedd_txt.append(' destroyQueueMemRefLists();') 996 gedd_txt.append('') 997 gedd_txt.append(' lock.unlock();') 998 gedd_txt.append('') 999 gedd_txt.append(' dispatch_key key = get_dispatch_key(device);') 1000 gedd_txt.append(' VkLayerDispatchTable *pDisp = get_dispatch_table(object_tracker_device_table_map, device);') 1001 gedd_txt.append(' pDisp->DestroyDevice(device, pAllocator);') 1002 gedd_txt.append(' object_tracker_device_table_map.erase(key);') 1003 gedd_txt.append('') 1004 gedd_txt.append('}') 1005 gedd_txt.append('') 1006 return "\n".join(gedd_txt) 1007 1008 # Special-case validating some objects -- they may be non-NULL but should 1009 # only be validated upon meeting some condition specified below. 1010 def _dereference_conditionally(self, indent, prefix, type_name, name): 1011 s_code = '' 1012 if type_name == 'pBufferInfo': 1013 s_code += '%sif ((%sdescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) ||\n' % (indent, prefix) 1014 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) ||\n' % (indent, prefix) 1015 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||\n' % (indent, prefix) 1016 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) ) {\n' % (indent, prefix) 1017 elif type_name == 'pImageInfo': 1018 s_code += '%sif ((%sdescriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) ||\n' % (indent, prefix) 1019 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ||\n' % (indent, prefix) 1020 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) ||\n' % (indent, prefix) 1021 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) ||\n' % (indent, prefix) 1022 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) ) {\n' % (indent, prefix) 1023 elif type_name == 'pTexelBufferView': 1024 s_code += '%sif ((%sdescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) ||\n' % (indent, prefix) 1025 s_code += '%s (%sdescriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) ) {\n' % (indent, prefix) 1026 elif name == 'pBeginInfo->pInheritanceInfo': 1027 s_code += '%sOBJTRACK_NODE* pNode = VkCommandBufferMap[(uint64_t)commandBuffer];\n' % (indent) 1028 s_code += '%sif ((%s) && (pNode->status & OBJSTATUS_COMMAND_BUFFER_SECONDARY)) {\n' % (indent, name) 1029 else: 1030 s_code += '%sif (%s) {\n' % (indent, name) 1031 return s_code 1032 1033 def _gen_obj_validate_code(self, struct_uses, obj_type_mapping, func_name, valid_null_dict, param0_name, indent, prefix, array_index): 1034 pre_code = '' 1035 for obj in sorted(struct_uses): 1036 name = obj 1037 array = '' 1038 type_name = '' 1039 if '[' in obj: 1040 (name, array) = obj.split('[') 1041 type_name = name 1042 array = array.strip(']') 1043 if isinstance(struct_uses[obj], dict): 1044 local_prefix = '' 1045 name = '%s%s' % (prefix, name) 1046 ptr_type = False 1047 if 'p' == obj[0]: 1048 ptr_type = True 1049 tmp_pre = self._dereference_conditionally(indent, prefix, type_name, name) 1050 pre_code += tmp_pre 1051 indent += ' ' 1052 if array != '': 1053 idx = 'idx%s' % str(array_index) 1054 array_index += 1 1055 pre_code += '%s\n' % self.lineinfo.get() 1056 pre_code += '%sfor (uint32_t %s=0; %s<%s%s; ++%s) {\n' % (indent, idx, idx, prefix, array, idx) 1057 indent += ' ' 1058 local_prefix = '%s[%s].' % (name, idx) 1059 elif ptr_type: 1060 local_prefix = '%s->' % (name) 1061 else: 1062 local_prefix = '%s.' % (name) 1063 tmp_pre = self._gen_obj_validate_code(struct_uses[obj], obj_type_mapping, func_name, valid_null_dict, param0_name, indent, local_prefix, array_index) 1064 pre_code += tmp_pre 1065 if array != '': 1066 indent = indent[4:] 1067 pre_code += '%s}\n' % (indent) 1068 if ptr_type: 1069 indent = indent[4:] 1070 pre_code += '%s}\n' % (indent) 1071 else: 1072 ptype = struct_uses[obj] 1073 dbg_obj_type = obj_type_mapping[ptype] 1074 fname = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', ptype) 1075 fname = re.sub('([a-z0-9])([A-Z])', r'\1_\2', fname).lower()[3:] 1076 full_name = '%s%s' % (prefix, name) 1077 null_obj_ok = 'false' 1078 # If a valid null param is defined for this func and we have a match, allow NULL 1079 if func_name in valid_null_dict and True in [name in pn for pn in sorted(valid_null_dict[func_name])]: 1080 null_obj_ok = 'true' 1081 if (array_index > 0) or '' != array: 1082 tmp_pre = self._dereference_conditionally(indent, prefix, type_name, full_name) 1083 pre_code += tmp_pre 1084 indent += ' ' 1085 if array != '': 1086 idx = 'idx%s' % str(array_index) 1087 array_index += 1 1088 pre_code += '%sfor (uint32_t %s=0; %s<%s%s; ++%s) {\n' % (indent, idx, idx, prefix, array, idx) 1089 indent += ' ' 1090 full_name = '%s[%s]' % (full_name, idx) 1091 pre_code += '%s\n' % self.lineinfo.get() 1092 pre_code += '%sskipCall |= validate_%s(%s, %s, %s, %s);\n' %(indent, fname, param0_name, full_name, dbg_obj_type, null_obj_ok) 1093 if array != '': 1094 indent = indent[4:] 1095 pre_code += '%s}\n' % (indent) 1096 indent = indent[4:] 1097 pre_code += '%s}\n' % (indent) 1098 else: 1099 pre_code += '%s\n' % self.lineinfo.get() 1100 pre_code += '%sskipCall |= validate_%s(%s, %s, %s, %s);\n' %(indent, fname, param0_name, full_name, dbg_obj_type, null_obj_ok) 1101 return pre_code 1102 1103 def generate_intercept(self, proto, qual): 1104 if proto.name in [ 'CreateDebugReportCallbackEXT', 'EnumerateInstanceLayerProperties', 'EnumerateInstanceExtensionProperties','EnumerateDeviceLayerProperties', 'EnumerateDeviceExtensionProperties' ]: 1105 # use default version 1106 return None 1107 1108 # Create map of object names to object type enums of the form VkName : VkObjectTypeName 1109 obj_type_mapping = {base_t : base_t.replace("Vk", "VkDebugReportObjectType") for base_t in vulkan.object_type_list} 1110 # Convert object type enum names from UpperCamelCase to UPPER_CASE_WITH_UNDERSCORES 1111 for objectName, objectTypeEnum in obj_type_mapping.items(): 1112 obj_type_mapping[objectName] = ucc_to_U_C_C(objectTypeEnum) + '_EXT'; 1113 # Command Buffer Object doesn't follow the rule. 1114 obj_type_mapping['VkCommandBuffer'] = "VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT" 1115 obj_type_mapping['VkShaderModule'] = "VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT" 1116 1117 explicit_object_tracker_functions = [ 1118 "CreateInstance", 1119 "EnumeratePhysicalDevices", 1120 "GetPhysicalDeviceQueueFamilyProperties", 1121 "CreateDevice", 1122 "GetDeviceQueue", 1123 "QueueBindSparse", 1124 "AllocateDescriptorSets", 1125 "FreeDescriptorSets", 1126 "CreateGraphicsPipelines", 1127 "CreateComputePipelines", 1128 "AllocateCommandBuffers", 1129 "FreeCommandBuffers", 1130 "DestroyDescriptorPool", 1131 "DestroyCommandPool", 1132 "MapMemory", 1133 "UnmapMemory", 1134 "FreeMemory", 1135 "DestroySwapchainKHR", 1136 "GetSwapchainImagesKHR" 1137 ] 1138 decl = proto.c_func(attr="VKAPI") 1139 param0_name = proto.params[0].name 1140 using_line = '' 1141 create_line = '' 1142 destroy_line = '' 1143 # Dict below tracks params that are vk objects. Dict is "loop count"->["params w/ that loop count"] where '0' is params that aren't in an array 1144 # TODO : Should integrate slightly better code for this purpose from unique_objects layer 1145 loop_params = defaultdict(list) # Dict uses loop count as key to make final code generation cleaner so params shared in single loop where needed 1146 loop_types = defaultdict(list) 1147 # TODO : For now skipping objs that can be NULL. Really should check these and have special case that allows them to be NULL 1148 # or better yet, these should be encoded into an API json definition and we generate checks from there 1149 # Until then, this is a dict where each func name is a list of object params that can be null (so don't need to be validated) 1150 # param names may be directly passed to the function, or may be a field in a struct param 1151 valid_null_object_names = {'CreateGraphicsPipelines' : ['basePipelineHandle'], 1152 'CreateComputePipelines' : ['basePipelineHandle'], 1153 'BeginCommandBuffer' : ['renderPass', 'framebuffer'], 1154 'QueueSubmit' : ['fence'], 1155 'AcquireNextImageKHR' : ['fence', 'semaphore' ], 1156 'UpdateDescriptorSets' : ['pTexelBufferView'], 1157 'CreateSwapchainKHR' : ['oldSwapchain'], 1158 } 1159 param_count = 'NONE' # keep track of arrays passed directly into API functions 1160 for p in proto.params: 1161 base_type = p.ty.replace('const ', '').strip('*') 1162 if 'count' in p.name.lower(): 1163 param_count = p.name 1164 if base_type in vulkan.core.objects: 1165 # This is an object to potentially check for validity. First see if it's an array 1166 if '*' in p.ty and 'const' in p.ty and param_count != 'NONE': 1167 loop_params[param_count].append(p.name) 1168 loop_types[param_count].append(str(p.ty[6:-1])) 1169 # Not an array, check for just a base Object that's not in exceptions 1170 elif '*' not in p.ty and (proto.name not in valid_null_object_names or p.name not in valid_null_object_names[proto.name]): 1171 loop_params[0].append(p.name) 1172 loop_types[0].append(str(p.ty)) 1173 elif vk_helper.is_type(base_type, 'struct'): 1174 struct_type = base_type 1175 if vk_helper.typedef_rev_dict[struct_type] in vk_helper.struct_dict: 1176 struct_type = vk_helper.typedef_rev_dict[struct_type] 1177 # Parse elements of this struct param to identify objects and/or arrays of objects 1178 for m in sorted(vk_helper.struct_dict[struct_type]): 1179 if vk_helper.struct_dict[struct_type][m]['type'] in vulkan.core.objects and vk_helper.struct_dict[struct_type][m]['type'] not in ['VkPhysicalDevice', 'VkQueue', 'VkFence', 'VkImage', 'VkDeviceMemory']: 1180 if proto.name not in valid_null_object_names or vk_helper.struct_dict[struct_type][m]['name'] not in valid_null_object_names[proto.name]: 1181 # This is not great, but gets the job done for now, but If we have a count and this param is a ptr w/ 1182 # last letter 's' OR non-'count' string of count is in the param name, then this is a dynamically sized array param 1183 param_array = False 1184 if param_count != 'NONE': 1185 if '*' in p.ty: 1186 if 's' == p.name[-1] or param_count.lower().replace('count', '') in p.name.lower(): 1187 param_array = True 1188 if param_array: 1189 param_name = '%s[i].%s' % (p.name, vk_helper.struct_dict[struct_type][m]['name']) 1190 else: 1191 param_name = '%s->%s' % (p.name, vk_helper.struct_dict[struct_type][m]['name']) 1192 if vk_helper.struct_dict[struct_type][m]['dyn_array']: 1193 if param_count != 'NONE': # this will be a double-embedded loop, use comma delineated 'count,name' for param_name 1194 loop_count = '%s[i].%s' % (p.name, vk_helper.struct_dict[struct_type][m]['array_size']) 1195 loop_params[param_count].append('%s,%s' % (loop_count, param_name)) 1196 loop_types[param_count].append('%s' % (vk_helper.struct_dict[struct_type][m]['type'])) 1197 else: 1198 loop_count = '%s->%s' % (p.name, vk_helper.struct_dict[struct_type][m]['array_size']) 1199 loop_params[loop_count].append(param_name) 1200 loop_types[loop_count].append('%s' % (vk_helper.struct_dict[struct_type][m]['type'])) 1201 else: 1202 if '[' in param_name: # dynamic array param, set size 1203 loop_params[param_count].append(param_name) 1204 loop_types[param_count].append('%s' % (vk_helper.struct_dict[struct_type][m]['type'])) 1205 else: 1206 loop_params[0].append(param_name) 1207 loop_types[0].append('%s' % (vk_helper.struct_dict[struct_type][m]['type'])) 1208 last_param_index = None 1209 create_func = False 1210 if True in [create_txt in proto.name for create_txt in ['Create', 'Allocate']]: 1211 create_func = True 1212 last_param_index = -1 # For create funcs don't validate last object 1213 (struct_uses, local_decls) = get_object_uses(vulkan.object_type_list, proto.params[:last_param_index]) 1214 funcs = [] 1215 mutex_unlock = False 1216 funcs.append('%s\n' % self.lineinfo.get()) 1217 if proto.name in explicit_object_tracker_functions: 1218 funcs.append('%s%s\n' 1219 '{\n' 1220 ' return explicit_%s;\n' 1221 '}' % (qual, decl, proto.c_call())) 1222 return "".join(funcs) 1223 # Temporarily prevent DestroySurface call from being generated until WSI layer support is fleshed out 1224 elif 'DestroyInstance' in proto.name or 'DestroyDevice' in proto.name: 1225 return "" 1226 else: 1227 if create_func: 1228 typ = proto.params[-1].ty.strip('*').replace('const ', ''); 1229 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', typ) 1230 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 1231 create_line = ' {\n' 1232 create_line += ' std::lock_guard<std::mutex> lock(global_lock);\n' 1233 create_line += ' if (result == VK_SUCCESS) {\n' 1234 create_line += ' create_%s(%s, *%s, %s);\n' % (name, param0_name, proto.params[-1].name, obj_type_mapping[typ]) 1235 create_line += ' }\n' 1236 create_line += ' }\n' 1237 if 'FreeCommandBuffers' in proto.name: 1238 typ = proto.params[-1].ty.strip('*').replace('const ', ''); 1239 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', typ) 1240 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 1241 funcs.append('%s\n' % self.lineinfo.get()) 1242 destroy_line = ' loader_platform_thread_lock_mutex(&objLock);\n' 1243 destroy_line += ' for (uint32_t i = 0; i < commandBufferCount; i++) {\n' 1244 destroy_line += ' destroy_%s(%s[i], %s[i]);\n' % (name, proto.params[-1].name, proto.params[-1].name) 1245 destroy_line += ' }\n' 1246 destroy_line += ' loader_platform_thread_unlock_mutex(&objLock);\n' 1247 if 'Destroy' in proto.name: 1248 typ = proto.params[-2].ty.strip('*').replace('const ', ''); 1249 name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', typ) 1250 name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()[3:] 1251 funcs.append('%s\n' % self.lineinfo.get()) 1252 destroy_line = ' {\n' 1253 destroy_line += ' std::lock_guard<std::mutex> lock(global_lock);\n' 1254 destroy_line += ' destroy_%s(%s, %s);\n' % (name, param0_name, proto.params[-2].name) 1255 destroy_line += ' }\n' 1256 indent = ' ' 1257 if len(struct_uses) > 0: 1258 using_line += '%sbool skipCall = false;\n' % (indent) 1259 if not mutex_unlock: 1260 using_line += '%s{\n' % (indent) 1261 indent += ' ' 1262 using_line += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent) 1263 mutex_unlock = True 1264 using_line += '// objects to validate: %s\n' % str(sorted(struct_uses)) 1265 using_line += self._gen_obj_validate_code(struct_uses, obj_type_mapping, proto.name, valid_null_object_names, param0_name, indent, '', 0) 1266 if mutex_unlock: 1267 indent = indent[4:] 1268 using_line += '%s}\n' % (indent) 1269 if len(struct_uses) > 0: 1270 using_line += ' if (skipCall)\n' 1271 if proto.ret == "bool": 1272 using_line += ' return false;\n' 1273 elif proto.ret == "VkBool32": 1274 using_line += ' return VK_FALSE;\n' 1275 elif proto.ret != "void": 1276 using_line += ' return VK_ERROR_VALIDATION_FAILED_EXT;\n' 1277 else: 1278 using_line += ' return;\n' 1279 ret_val = '' 1280 stmt = '' 1281 if proto.ret != "void": 1282 ret_val = "%s result = " % proto.ret 1283 stmt = " return result;\n" 1284 1285 dispatch_param = proto.params[0].name 1286 if 'CreateInstance' in proto.name: 1287 dispatch_param = '*' + proto.params[1].name 1288 1289 # Must use 'instance' table for these APIs, 'device' table otherwise 1290 table_type = "" 1291 if proto_is_global(proto): 1292 table_type = "instance" 1293 else: 1294 table_type = "device" 1295 if wsi_name(proto.name): 1296 funcs.append('%s' % wsi_ifdef(proto.name)) 1297 funcs.append('%s%s\n' 1298 '{\n' 1299 '%s' 1300 '%s' 1301 ' %sget_dispatch_table(object_tracker_%s_table_map, %s)->%s;\n' 1302 '%s' 1303 '%s' 1304 '}' % (qual, decl, using_line, destroy_line, ret_val, table_type, dispatch_param, proto.c_call(), create_line, stmt)) 1305 if wsi_name(proto.name): 1306 funcs.append('%s' % wsi_endif(proto.name)) 1307 return "\n\n".join(funcs) 1308 1309 def generate_body(self): 1310 self.layer_name = "object_tracker" 1311 extensions=[('wsi_enabled', 1312 ['vkCreateSwapchainKHR', 1313 'vkDestroySwapchainKHR', 'vkGetSwapchainImagesKHR', 1314 'vkAcquireNextImageKHR', 'vkQueuePresentKHR'])] 1315 if self.wsi == 'Win32': 1316 instance_extensions=[('msg_callback_get_proc_addr', []), 1317 ('wsi_enabled', 1318 ['vkDestroySurfaceKHR', 1319 'vkGetPhysicalDeviceSurfaceSupportKHR', 1320 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1321 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1322 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1323 'vkCreateWin32SurfaceKHR', 1324 'vkGetPhysicalDeviceWin32PresentationSupportKHR'])] 1325 elif self.wsi == 'Android': 1326 instance_extensions=[('msg_callback_get_proc_addr', []), 1327 ('wsi_enabled', 1328 ['vkDestroySurfaceKHR', 1329 'vkGetPhysicalDeviceSurfaceSupportKHR', 1330 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1331 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1332 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1333 'vkCreateAndroidSurfaceKHR'])] 1334 elif self.wsi == 'Xcb' or self.wsi == 'Xlib' or self.wsi == 'Wayland' or self.wsi == 'Mir': 1335 instance_extensions=[('msg_callback_get_proc_addr', []), 1336 ('wsi_enabled', 1337 ['vkDestroySurfaceKHR', 1338 'vkGetPhysicalDeviceSurfaceSupportKHR', 1339 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1340 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1341 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1342 'vkCreateXcbSurfaceKHR', 1343 'vkGetPhysicalDeviceXcbPresentationSupportKHR', 1344 'vkCreateXlibSurfaceKHR', 1345 'vkGetPhysicalDeviceXlibPresentationSupportKHR', 1346 'vkCreateWaylandSurfaceKHR', 1347 'vkGetPhysicalDeviceWaylandPresentationSupportKHR', 1348 'vkCreateMirSurfaceKHR', 1349 'vkGetPhysicalDeviceMirPresentationSupportKHR'])] 1350 else: 1351 print('Error: Undefined DisplayServer') 1352 instance_extensions=[] 1353 1354 body = ["namespace %s {" % self.layer_name, 1355 self.generate_maps(), 1356 self.generate_procs(), 1357 self.generate_destroy_instance(), 1358 self.generate_destroy_device(), 1359 self._generate_dispatch_entrypoints(), 1360 self._generate_extensions(), 1361 self._generate_layer_gpa_function(extensions, 1362 instance_extensions), 1363 "} // namespace %s" % self.layer_name, 1364 self._gen_layer_logging_workaround(), 1365 self._gen_layer_interface_v0_functions()] 1366 return "\n\n".join(body) 1367 1368 class UniqueObjectsSubcommand(Subcommand): 1369 def generate_header(self): 1370 header_txt = [] 1371 header_txt.append('%s' % self.lineinfo.get()) 1372 header_txt.append('#include "unique_objects.h"') 1373 return "\n".join(header_txt) 1374 1375 # Generate UniqueObjects code for given struct_uses dict of objects that need to be unwrapped 1376 # vector_name_set is used to make sure we don't replicate vector names 1377 # first_level_param indicates if elements are passed directly into the function else they're below a ptr/struct 1378 # TODO : Comment this code 1379 def _gen_obj_code(self, struct_uses, param_type, indent, prefix, array_index, vector_name_set, first_level_param): 1380 decls = '' 1381 pre_code = '' 1382 post_code = '' 1383 for obj in sorted(struct_uses): 1384 name = obj 1385 array = '' 1386 if '[' in obj: 1387 (name, array) = obj.split('[') 1388 array = array.strip(']') 1389 ptr_type = False 1390 if 'p' == obj[0] and obj[1] != obj[1].lower(): # TODO : Not ideal way to determine ptr 1391 ptr_type = True 1392 if isinstance(struct_uses[obj], dict): 1393 local_prefix = '' 1394 name = '%s%s' % (prefix, name) 1395 if ptr_type: 1396 if first_level_param and name in param_type: 1397 pre_code += '%sif (%s) {\n' % (indent, name) 1398 else: # shadow ptr will have been initialized at this point so check it vs. source ptr 1399 pre_code += '%sif (local_%s) {\n' % (indent, name) 1400 indent += ' ' 1401 if array != '': 1402 idx = 'idx%s' % str(array_index) 1403 array_index += 1 1404 if first_level_param and name in param_type: 1405 pre_code += '%slocal_%s = new safe_%s[%s];\n' % (indent, name, param_type[name].strip('*'), array) 1406 post_code += ' if (local_%s)\n' % (name) 1407 post_code += ' delete[] local_%s;\n' % (name) 1408 pre_code += '%sfor (uint32_t %s=0; %s<%s%s; ++%s) {\n' % (indent, idx, idx, prefix, array, idx) 1409 indent += ' ' 1410 if first_level_param: 1411 pre_code += '%slocal_%s[%s].initialize(&%s[%s]);\n' % (indent, name, idx, name, idx) 1412 local_prefix = '%s[%s].' % (name, idx) 1413 elif ptr_type: 1414 if first_level_param and name in param_type: 1415 pre_code += '%slocal_%s = new safe_%s(%s);\n' % (indent, name, param_type[name].strip('*'), name) 1416 post_code += ' if (local_%s)\n' % (name) 1417 post_code += ' delete local_%s;\n' % (name) 1418 local_prefix = '%s->' % (name) 1419 else: 1420 local_prefix = '%s.' % (name) 1421 assert isinstance(decls, object) 1422 (tmp_decl, tmp_pre, tmp_post) = self._gen_obj_code(struct_uses[obj], param_type, indent, local_prefix, array_index, vector_name_set, False) 1423 decls += tmp_decl 1424 pre_code += tmp_pre 1425 post_code += tmp_post 1426 if array != '': 1427 indent = indent[4:] 1428 pre_code += '%s}\n' % (indent) 1429 if ptr_type: 1430 indent = indent[4:] 1431 pre_code += '%s}\n' % (indent) 1432 else: 1433 if (array_index > 0) or array != '': # TODO : This is not ideal, really want to know if we're anywhere under an array 1434 if first_level_param: 1435 decls += '%s%s* local_%s = NULL;\n' % (indent, struct_uses[obj], name) 1436 if array != '' and not first_level_param: # ptrs under structs will have been initialized so use local_* 1437 pre_code += '%sif (local_%s%s) {\n' %(indent, prefix, name) 1438 else: 1439 pre_code += '%sif (%s%s) {\n' %(indent, prefix, name) 1440 indent += ' ' 1441 if array != '': 1442 idx = 'idx%s' % str(array_index) 1443 array_index += 1 1444 if first_level_param: 1445 pre_code += '%slocal_%s = new %s[%s];\n' % (indent, name, struct_uses[obj], array) 1446 post_code += ' if (local_%s)\n' % (name) 1447 post_code += ' delete[] local_%s;\n' % (name) 1448 pre_code += '%sfor (uint32_t %s=0; %s<%s%s; ++%s) {\n' % (indent, idx, idx, prefix, array, idx) 1449 indent += ' ' 1450 name = '%s[%s]' % (name, idx) 1451 pName = 'p%s' % (struct_uses[obj][2:]) 1452 if name not in vector_name_set: 1453 vector_name_set.add(name) 1454 pre_code += '%slocal_%s%s = (%s)my_map_data->unique_id_mapping[reinterpret_cast<const uint64_t &>(%s%s)];\n' % (indent, prefix, name, struct_uses[obj], prefix, name) 1455 if array != '': 1456 indent = indent[4:] 1457 pre_code += '%s}\n' % (indent) 1458 indent = indent[4:] 1459 pre_code += '%s}\n' % (indent) 1460 else: 1461 pre_code += '%s\n' % (self.lineinfo.get()) 1462 deref_txt = '&' 1463 if ptr_type: 1464 deref_txt = '' 1465 if '->' in prefix: # need to update local struct 1466 pre_code += '%slocal_%s%s = (%s)my_map_data->unique_id_mapping[reinterpret_cast<const uint64_t &>(%s%s)];\n' % (indent, prefix, name, struct_uses[obj], prefix, name) 1467 else: 1468 pre_code += '%s%s = (%s)my_map_data->unique_id_mapping[reinterpret_cast<uint64_t &>(%s)];\n' % (indent, name, struct_uses[obj], name) 1469 return decls, pre_code, post_code 1470 1471 def generate_intercept(self, proto, qual): 1472 create_func = False 1473 destroy_func = False 1474 last_param_index = None #typcially we look at all params for ndos 1475 pre_call_txt = '' # code prior to calling down chain such as unwrap uses of ndos 1476 post_call_txt = '' # code following call down chain such to wrap newly created ndos, or destroy local wrap struct 1477 funcs = [] 1478 indent = ' ' # indent level for generated code 1479 decl = proto.c_func(attr="VKAPI") 1480 # A few API cases that are manual code 1481 # TODO : Special case Create*Pipelines funcs to handle creating multiple unique objects 1482 explicit_object_tracker_functions = ['GetSwapchainImagesKHR', 1483 'CreateSwapchainKHR', 1484 'CreateInstance', 1485 'DestroyInstance', 1486 'CreateDevice', 1487 'DestroyDevice', 1488 'CreateComputePipelines', 1489 'CreateGraphicsPipelines' 1490 ] 1491 # TODO : This is hacky, need to make this a more general-purpose solution for all layers 1492 ifdef_dict = {'CreateXcbSurfaceKHR': 'VK_USE_PLATFORM_XCB_KHR', 1493 'CreateAndroidSurfaceKHR': 'VK_USE_PLATFORM_ANDROID_KHR', 1494 'CreateWin32SurfaceKHR': 'VK_USE_PLATFORM_WIN32_KHR', 1495 'CreateXlibSurfaceKHR': 'VK_USE_PLATFORM_XLIB_KHR', 1496 'CreateWaylandSurfaceKHR': 'VK_USE_PLATFORM_WAYLAND_KHR', 1497 'CreateMirSurfaceKHR': 'VK_USE_PLATFORM_MIR_KHR'} 1498 # Give special treatment to create functions that return multiple new objects 1499 # This dict stores array name and size of array 1500 custom_create_dict = {'pDescriptorSets' : 'pAllocateInfo->descriptorSetCount'} 1501 pre_call_txt += '%s\n' % (self.lineinfo.get()) 1502 if proto.name in explicit_object_tracker_functions: 1503 funcs.append('%s%s\n' 1504 '{\n' 1505 ' return explicit_%s;\n' 1506 '}' % (qual, decl, proto.c_call())) 1507 return "".join(funcs) 1508 if True in [create_txt in proto.name for create_txt in ['Create', 'Allocate']]: 1509 create_func = True 1510 last_param_index = -1 # For create funcs don't care if last param is ndo 1511 if True in [destroy_txt in proto.name for destroy_txt in ['Destroy', 'Free']]: 1512 destroy_obj_type = proto.params[-2].ty 1513 if destroy_obj_type in vulkan.object_non_dispatch_list: 1514 destroy_func = True 1515 1516 # First thing we need to do is gather uses of non-dispatchable-objects (ndos) 1517 (struct_uses, local_decls) = get_object_uses(vulkan.object_non_dispatch_list, proto.params[1:last_param_index]) 1518 1519 dispatch_param = proto.params[0].name 1520 if 'CreateInstance' in proto.name: 1521 dispatch_param = '*' + proto.params[1].name 1522 pre_call_txt += '%slayer_data *my_map_data = get_my_data_ptr(get_dispatch_key(%s), layer_data_map);\n' % (indent, dispatch_param) 1523 if len(struct_uses) > 0: 1524 pre_call_txt += '// STRUCT USES:%s\n' % sorted(struct_uses) 1525 if len(local_decls) > 0: 1526 pre_call_txt += '//LOCAL DECLS:%s\n' % sorted(local_decls) 1527 if destroy_func: # only one object 1528 pre_call_txt += '%sstd::unique_lock<std::mutex> lock(global_lock);\n' % (indent) 1529 for del_obj in sorted(struct_uses): 1530 pre_call_txt += '%suint64_t local_%s = reinterpret_cast<uint64_t &>(%s);\n' % (indent, del_obj, del_obj) 1531 pre_call_txt += '%s%s = (%s)my_map_data->unique_id_mapping[local_%s];\n' % (indent, del_obj, struct_uses[del_obj], del_obj) 1532 pre_call_txt += '%slock.unlock();\n' % (indent) 1533 (pre_decl, pre_code, post_code) = ('', '', '') 1534 else: 1535 (pre_decl, pre_code, post_code) = self._gen_obj_code(struct_uses, local_decls, ' ', '', 0, set(), True) 1536 # This is a bit hacky but works for now. Need to decl local versions of top-level structs 1537 for ld in sorted(local_decls): 1538 init_null_txt = 'NULL'; 1539 if '*' not in local_decls[ld]: 1540 init_null_txt = '{}'; 1541 if local_decls[ld].strip('*') not in vulkan.object_non_dispatch_list: 1542 pre_decl += ' safe_%s local_%s = %s;\n' % (local_decls[ld], ld, init_null_txt) 1543 if pre_code != '': # lock around map uses 1544 pre_code = '%s{\n%sstd::lock_guard<std::mutex> lock(global_lock);\n%s%s}\n' % (indent, indent, pre_code, indent) 1545 pre_call_txt += '%s%s' % (pre_decl, pre_code) 1546 post_call_txt += '%s' % (post_code) 1547 elif create_func: 1548 base_type = proto.params[-1].ty.replace('const ', '').strip('*') 1549 if base_type not in vulkan.object_non_dispatch_list: 1550 return None 1551 else: 1552 return None 1553 1554 ret_val = '' 1555 ret_stmt = '' 1556 if proto.ret != "void": 1557 ret_val = "%s result = " % proto.ret 1558 ret_stmt = " return result;\n" 1559 if create_func: 1560 obj_type = proto.params[-1].ty.strip('*') 1561 obj_name = proto.params[-1].name 1562 if obj_type in vulkan.object_non_dispatch_list: 1563 local_name = "unique%s" % obj_type[2:] 1564 post_call_txt += '%sif (VK_SUCCESS == result) {\n' % (indent) 1565 indent += ' ' 1566 post_call_txt += '%sstd::lock_guard<std::mutex> lock(global_lock);\n' % (indent) 1567 if obj_name in custom_create_dict: 1568 post_call_txt += '%s\n' % (self.lineinfo.get()) 1569 local_name = '%ss' % (local_name) # add 's' to end for vector of many 1570 post_call_txt += '%sfor (uint32_t i=0; i<%s; ++i) {\n' % (indent, custom_create_dict[obj_name]) 1571 indent += ' ' 1572 post_call_txt += '%suint64_t unique_id = global_unique_id++;\n' % (indent) 1573 post_call_txt += '%smy_map_data->unique_id_mapping[unique_id] = reinterpret_cast<uint64_t &>(%s[i]);\n' % (indent, obj_name) 1574 post_call_txt += '%s%s[i] = reinterpret_cast<%s&>(unique_id);\n' % (indent, obj_name, obj_type) 1575 indent = indent[4:] 1576 post_call_txt += '%s}\n' % (indent) 1577 else: 1578 post_call_txt += '%s\n' % (self.lineinfo.get()) 1579 post_call_txt += '%suint64_t unique_id = global_unique_id++;\n' % (indent) 1580 post_call_txt += '%smy_map_data->unique_id_mapping[unique_id] = reinterpret_cast<uint64_t &>(*%s);\n' % (indent, obj_name) 1581 post_call_txt += '%s*%s = reinterpret_cast<%s&>(unique_id);\n' % (indent, obj_name, obj_type) 1582 indent = indent[4:] 1583 post_call_txt += '%s}\n' % (indent) 1584 elif destroy_func: 1585 del_obj = proto.params[-2].name 1586 if 'count' in del_obj.lower(): 1587 post_call_txt += '%s\n' % (self.lineinfo.get()) 1588 post_call_txt += '%sfor (uint32_t i=0; i<%s; ++i) {\n' % (indent, del_obj) 1589 del_obj = proto.params[-1].name 1590 indent += ' ' 1591 post_call_txt += '%sdelete (VkUniqueObject*)%s[i];\n' % (indent, del_obj) 1592 indent = indent[4:] 1593 post_call_txt += '%s}\n' % (indent) 1594 else: 1595 post_call_txt += '%s\n' % (self.lineinfo.get()) 1596 post_call_txt += '%slock.lock();\n' % (indent) 1597 post_call_txt += '%smy_map_data->unique_id_mapping.erase(local_%s);\n' % (indent, proto.params[-2].name) 1598 1599 call_sig = proto.c_call() 1600 # Replace default params with any custom local params 1601 for ld in local_decls: 1602 call_sig = call_sig.replace(ld, '(const %s)local_%s' % (local_decls[ld], ld)) 1603 if proto_is_global(proto): 1604 table_type = "instance" 1605 else: 1606 table_type = "device" 1607 pre_call_txt += '%s\n' % (self.lineinfo.get()) 1608 open_ifdef = '' 1609 close_ifdef = '' 1610 if proto.name in ifdef_dict: 1611 open_ifdef = '#ifdef %s\n' % (ifdef_dict[proto.name]) 1612 close_ifdef = '#endif\n' 1613 funcs.append('%s' 1614 '%s%s\n' 1615 '{\n' 1616 '%s' 1617 ' %sget_dispatch_table(unique_objects_%s_table_map, %s)->%s;\n' 1618 '%s' 1619 '%s' 1620 '}\n' 1621 '%s' % (open_ifdef, qual, decl, pre_call_txt, ret_val, table_type, dispatch_param, call_sig, post_call_txt, ret_stmt, close_ifdef)) 1622 return "\n\n".join(funcs) 1623 1624 def generate_body(self): 1625 self.layer_name = "unique_objects" 1626 extensions=[('wsi_enabled', 1627 ['vkCreateSwapchainKHR', 1628 'vkDestroySwapchainKHR', 'vkGetSwapchainImagesKHR', 1629 'vkAcquireNextImageKHR', 'vkQueuePresentKHR'])] 1630 if self.wsi == 'Win32': 1631 instance_extensions=[('wsi_enabled', 1632 ['vkDestroySurfaceKHR', 1633 'vkGetPhysicalDeviceSurfaceSupportKHR', 1634 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1635 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1636 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1637 'vkCreateWin32SurfaceKHR' 1638 ])] 1639 elif self.wsi == 'Android': 1640 instance_extensions=[('wsi_enabled', 1641 ['vkDestroySurfaceKHR', 1642 'vkGetPhysicalDeviceSurfaceSupportKHR', 1643 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1644 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1645 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1646 'vkCreateAndroidSurfaceKHR'])] 1647 elif self.wsi == 'Xcb' or self.wsi == 'Xlib' or self.wsi == 'Wayland' or self.wsi == 'Mir': 1648 instance_extensions=[('wsi_enabled', 1649 ['vkDestroySurfaceKHR', 1650 'vkGetPhysicalDeviceSurfaceSupportKHR', 1651 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR', 1652 'vkGetPhysicalDeviceSurfaceFormatsKHR', 1653 'vkGetPhysicalDeviceSurfacePresentModesKHR', 1654 'vkCreateXcbSurfaceKHR', 1655 'vkCreateXlibSurfaceKHR', 1656 'vkCreateWaylandSurfaceKHR', 1657 'vkCreateMirSurfaceKHR' 1658 ])] 1659 else: 1660 print('Error: Undefined DisplayServer') 1661 instance_extensions=[] 1662 1663 body = ["namespace %s {" % self.layer_name, 1664 self._generate_dispatch_entrypoints(), 1665 self._generate_layer_gpa_function(extensions, 1666 instance_extensions), 1667 "} // namespace %s" % self.layer_name, 1668 self._gen_layer_interface_v0_functions()] 1669 return "\n\n".join(body) 1670 1671 def main(): 1672 wsi = { 1673 "Win32", 1674 "Android", 1675 "Xcb", 1676 "Xlib", 1677 "Wayland", 1678 "Mir", 1679 } 1680 1681 subcommands = { 1682 "object_tracker" : ObjectTrackerSubcommand, 1683 "unique_objects" : UniqueObjectsSubcommand, 1684 } 1685 1686 if len(sys.argv) < 4 or sys.argv[1] not in wsi or sys.argv[2] not in subcommands or not os.path.exists(sys.argv[3]): 1687 print("Usage: %s <wsi> <subcommand> <input_header> [outdir]" % sys.argv[0]) 1688 print 1689 print("Available subcommands are: %s" % " ".join(subcommands)) 1690 exit(1) 1691 1692 hfp = vk_helper.HeaderFileParser(sys.argv[3]) 1693 hfp.parse() 1694 vk_helper.enum_val_dict = hfp.get_enum_val_dict() 1695 vk_helper.enum_type_dict = hfp.get_enum_type_dict() 1696 vk_helper.struct_dict = hfp.get_struct_dict() 1697 vk_helper.typedef_fwd_dict = hfp.get_typedef_fwd_dict() 1698 vk_helper.typedef_rev_dict = hfp.get_typedef_rev_dict() 1699 vk_helper.types_dict = hfp.get_types_dict() 1700 1701 outfile = None 1702 if len(sys.argv) >= 5: 1703 outfile = sys.argv[4] 1704 1705 subcmd = subcommands[sys.argv[2]](outfile) 1706 subcmd.run() 1707 1708 if __name__ == "__main__": 1709 main() 1710