1# Copyright 2018 Google LLC 2# SPDX-License-Identifier: MIT 3 4from .common.codegen import CodeGen 5from .common.vulkantypes import \ 6 VulkanAPI, makeVulkanTypeSimple, iterateVulkanType 7 8from .wrapperdefs import VulkanWrapperGenerator 9 10# No real good way to automatically infer the most important Vulkan API 11# functions as it relates to which getProcAddress function to use, plus 12# we might want to control which function to use depending on our 13# performance needs. 14 15# This is based on the minimum set of functions needed to be directly 16# queried with dlsym and not returning null. 17getProcAddrFuncs = [ 18 "vkGetInstanceProcAddr", 19 "vkDestroyInstance", 20 "vkEnumeratePhysicalDevices", 21 "vkGetPhysicalDeviceFeatures", 22 "vkGetPhysicalDeviceFormatProperties", 23 "vkGetPhysicalDeviceImageFormatProperties", 24 "vkGetPhysicalDeviceProperties", 25 "vkGetPhysicalDeviceQueueFamilyProperties", 26 "vkGetPhysicalDeviceMemoryProperties", 27 "vkCreateDevice", 28 "vkDestroyDevice", 29 "vkEnumerateDeviceExtensionProperties", 30 "vkEnumerateDeviceLayerProperties", 31] 32 33# Some methods can only be found using dlsym() while we cannot get the function 34# address using vkGetInstProcAddr() or vkGetDeviceProcAddr(). These function 35# pointers should only be initialized when setting up the dispatch from system 36# loader. 37getProcAddrOnlyFuncs = [] 38 39getInstanceProcAddrNoInstanceFuncs = [ 40 "vkCreateInstance", 41 "vkEnumerateInstanceExtensionProperties", 42 "vkEnumerateInstanceLayerProperties", 43] 44 45getInstanceProcAddrFuncs = [ 46 "vkGetDeviceProcAddr", 47 "vkCreateSwapchainKHR", 48 "vkDestroySwapchainKHR", 49 "vkGetSwapchainImagesKHR", 50 "vkAcquireNextImageKHR", 51 "vkQueuePresentKHR", 52 "vkCreateMacOSSurfaceMVK", 53 "vkCreateWin32SurfaceKHR", 54 "vkGetPhysicalDeviceWin32PresentationSupportKHR", 55 "vkCreateXlibSurfaceKHR", 56 "vkGetPhysicalDeviceXlibPresentationSupportKHR", 57 "vkCreateXcbSurfaceKHR", 58 "vkGetPhysicalDeviceXcbPresentationSupportKHR", 59 "vkGetPhysicalDeviceSparseImageFormatProperties", 60 "vkEnumerateInstanceVersion", 61 "vkEnumeratePhysicalDeviceGroups", 62 "vkGetPhysicalDeviceFeatures2", 63 "vkGetPhysicalDeviceProperties2", 64 "vkGetPhysicalDeviceFormatProperties2", 65 "vkGetPhysicalDeviceImageFormatProperties2", 66 "vkGetPhysicalDeviceQueueFamilyProperties2", 67 "vkGetPhysicalDeviceMemoryProperties2", 68 "vkGetPhysicalDeviceSparseImageFormatProperties2", 69 "vkGetPhysicalDeviceExternalBufferProperties", 70 "vkGetPhysicalDeviceExternalFenceProperties", 71 "vkGetPhysicalDeviceExternalSemaphoreProperties", 72] 73 74# Implicitly, everything else is going to be obtained 75# with vkGetDeviceProcAddr, 76# unless it has instance in the arg. 77 78def isGetProcAddressAPI(vulkanApi): 79 return vulkanApi.name in getProcAddrFuncs 80 81def isGetProcAddressOnlyAPI(vulkanApi): 82 return vulkanApi.name in getProcAddrOnlyFuncs 83 84def isGetInstanceProcAddressNoInstanceAPI(vulkanApi): 85 return vulkanApi.name in getInstanceProcAddrNoInstanceFuncs 86 87def isGetInstanceProcAddressAPI(vulkanApi): 88 if vulkanApi.name in getInstanceProcAddrFuncs: 89 return True 90 91 if vulkanApi.parameters[0].typeName == "VkInstance": 92 return True 93 94 return False 95 96def isGetDeviceProcAddressAPI(vulkanApi): 97 if isGetProcAddressAPI(vulkanApi): 98 return False 99 100 if isGetProcAddressOnlyAPI(vulkanApi): 101 return False 102 103 if isGetInstanceProcAddressAPI(vulkanApi): 104 return False 105 106 return True 107 108def inferProcAddressFuncType(vulkanApi): 109 if isGetProcAddressAPI(vulkanApi): 110 return "global" 111 if isGetProcAddressOnlyAPI(vulkanApi): 112 return "global-only" 113 if isGetInstanceProcAddressNoInstanceAPI(vulkanApi): 114 return "global-instance" 115 if isGetInstanceProcAddressAPI(vulkanApi): 116 return "instance" 117 return "device" 118 119# VulkanDispatch defines a struct, VulkanDispatch, 120# that is populated by function pointers from the Vulkan 121# loader. No attempt is made to do something different 122# for instance vs device functions. 123class VulkanDispatch(VulkanWrapperGenerator): 124 def __init__(self, module, typeInfo): 125 VulkanWrapperGenerator.__init__(self, module, typeInfo) 126 127 self.apisToGet = {} 128 129 self.cgenHeader = CodeGen() 130 self.cgenImpl = CodeGen() 131 self.typeInfo = typeInfo 132 133 self.currentFeature = "" 134 self.featureForCodegen = "" 135 136 def onBegin(self): 137 138 # The first way is to use just the loader to get symbols. This doesn't 139 # necessarily work with extensions because at that point the dispatch 140 # table needs to be specific to a particular Vulkan instance or device. 141 142 self.cgenHeader.line(""" 143void init_vulkan_dispatch_from_system_loader( 144 DlOpenFunc dlOpenFunc, 145 DlSymFunc dlSymFunc, 146 VulkanDispatch* dispatch_out); 147""") 148 149 # The second way is to initialize the table from a given Vulkan 150 # instance or device. Provided the instance or device was created with 151 # the right extensions, we can obtain function pointers to extension 152 # functions this way. 153 154 self.cgenHeader.line(""" 155void init_vulkan_dispatch_from_instance( 156 VulkanDispatch* vk, 157 VkInstance instance, 158 VulkanDispatch* dispatch_out); 159""") 160 self.cgenHeader.line(""" 161void init_vulkan_dispatch_from_device( 162 VulkanDispatch* vk, 163 VkDevice device, 164 VulkanDispatch* dispatch_out); 165""") 166 167 # After populating a VulkanDispatch with the above methods, 168 # it can be useful to check whether the Vulkan 1.0 or 1.1 methods 169 # are all there. 170 def emit_feature_check_decl(cgen, tag, featureToCheck): 171 cgen.line(""" 172bool vulkan_dispatch_check_%s_%s( 173 const VulkanDispatch* vk); 174""" % (tag, featureToCheck)) 175 176 emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_0") 177 emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_1") 178 emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_0") 179 emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_1") 180 181 self.cgenHeader.line("struct VulkanDispatch {") 182 self.module.appendHeader(self.cgenHeader.swapCode()) 183 184 def syncFeatureQuiet(self, cgen, feature): 185 if self.featureForCodegen != feature: 186 if feature == "": 187 self.featureForCodegen = feature 188 return 189 190 self.featureForCodegen = feature 191 192 def syncFeature(self, cgen, feature): 193 if self.featureForCodegen != feature: 194 if feature == "": 195 cgen.leftline("#endif") 196 self.featureForCodegen = feature 197 return 198 199 if self.featureForCodegen != "": 200 cgen.leftline("#endif") 201 202 cgen.leftline("#ifdef %s" % feature) 203 self.featureForCodegen = feature 204 205 def makeDlsymCall(self, cgen, apiname, typedecl): 206 cgen.stmt( \ 207 "out->%s = (%s)dlSymFunc(lib, \"%s\")" % \ 208 (apiname, typedecl, apiname)) 209 210 def makeGetInstanceProcAddrCall(self, cgen, dispatch, instance, apiname, typedecl): 211 cgen.stmt( \ 212 "out->%s = (%s)%s->vkGetInstanceProcAddr(%s, \"%s\")" % \ 213 (apiname, typedecl, dispatch, instance, apiname)) 214 215 def makeGetDeviceProcAddrCall(self, cgen, dispatch, device, apiname, typedecl): 216 cgen.stmt( \ 217 "out->%s = (%s)%s->vkGetDeviceProcAddr(%s, \"%s\")" % \ 218 (apiname, typedecl, dispatch, device, apiname)) 219 220 def onEnd(self): 221 self.cgenHeader.line("};") 222 self.module.appendHeader(self.cgenHeader.swapCode()) 223 224 # Getting dispatch tables from the loader 225 self.cgenImpl.line(""" 226void init_vulkan_dispatch_from_system_loader( 227 DlOpenFunc dlOpenFunc, 228 DlSymFunc dlSymFunc, 229 VulkanDispatch* out)""") 230 231 self.cgenImpl.beginBlock() 232 233 self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") 234 235 self.cgenImpl.stmt("void* lib = dlOpenFunc()") 236 self.cgenImpl.stmt("if (!lib) return") 237 238 apis = \ 239 self.apisToGet["global"] + \ 240 self.apisToGet["global-instance"] + \ 241 self.apisToGet["instance"] + \ 242 self.apisToGet["device"] 243 244 if "global-only" in self.apisToGet: 245 apis = apis + self.apisToGet["global-only"] 246 247 for vulkanApi, typeDecl, feature in apis: 248 self.syncFeature(self.cgenImpl, feature) 249 self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) 250 251 self.syncFeature(self.cgenImpl, "") 252 self.cgenImpl.endBlock() 253 254 # Getting instance dispatch tables 255 self.cgenImpl.line(""" 256void init_vulkan_dispatch_from_instance( 257 VulkanDispatch* vk, 258 VkInstance instance, 259 VulkanDispatch* out)""") 260 261 self.cgenImpl.beginBlock() 262 263 self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") 264 265 apis = \ 266 self.apisToGet["global"] + \ 267 self.apisToGet["global-instance"] + \ 268 self.apisToGet["instance"] + \ 269 self.apisToGet["device"] 270 271 for vulkanApi, typeDecl, feature in apis: 272 self.syncFeature(self.cgenImpl, feature) 273 self.makeGetInstanceProcAddrCall( 274 self.cgenImpl, "vk", "instance", vulkanApi.name, typeDecl) 275 276 self.syncFeature(self.cgenImpl, "") 277 self.cgenImpl.endBlock() 278 279 # Getting device dispatch tables 280 self.cgenImpl.line(""" 281void init_vulkan_dispatch_from_device( 282 VulkanDispatch* vk, 283 VkDevice device, 284 VulkanDispatch* out)""") 285 286 self.cgenImpl.beginBlock() 287 288 self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") 289 290 apis = \ 291 self.apisToGet["global"] + \ 292 self.apisToGet["global-instance"] + \ 293 self.apisToGet["instance"] + \ 294 self.apisToGet["device"] 295 296 for vulkanApi, typeDecl, feature in apis: 297 self.syncFeature(self.cgenImpl, feature) 298 self.makeGetDeviceProcAddrCall( 299 self.cgenImpl, "vk", "device", vulkanApi.name, typeDecl) 300 301 self.syncFeature(self.cgenImpl, "") 302 self.cgenImpl.endBlock() 303 304 # Check Vulkan 1.0 / 1.1 functions 305 306 def emit_check_impl(cgen, dispatchVar, feature, featureToCheck, apiName): 307 if feature == featureToCheck: 308 cgen.beginIf("!%s->%s" % (dispatchVar, apiName)) 309 cgen.stmt("fprintf(stderr, \"%s check failed: %s not found\\n\")" % (featureToCheck, apiName)) 310 cgen.stmt("good = false") 311 cgen.endIf() 312 313 def emit_feature_check_impl(context, cgen, tag, featureToCheck, apis): 314 cgen.line(""" 315bool vulkan_dispatch_check_%s_%s( 316 const VulkanDispatch* vk) 317""" % (tag, featureToCheck)) 318 319 cgen.beginBlock() 320 321 cgen.stmt("bool good = true") 322 323 for vulkanApi, typeDecl, feature in apis: 324 context.syncFeatureQuiet(self.cgenImpl, feature) 325 emit_check_impl(cgen, "vk", feature, featureToCheck, vulkanApi.name) 326 327 context.syncFeatureQuiet(self.cgenImpl, "") 328 329 cgen.stmt("return good") 330 cgen.endBlock() 331 332 instanceApis = self.apisToGet["global-instance"] + self.apisToGet["instance"] 333 334 emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_0", instanceApis) 335 emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_1", instanceApis) 336 emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_0", self.apisToGet["device"]) 337 emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_1", self.apisToGet["device"]) 338 339 self.module.appendImpl(self.cgenImpl.swapCode()) 340 341 def onBeginFeature(self, featureName, featureType): 342 self.currentFeature = featureName 343 344 def onGenType(self, typeXml, name, alias): 345 VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) 346 347 def onGenCmd(self, cmdinfo, name, alias): 348 VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) 349 350 vulkanApi = self.typeInfo.apis[name] 351 352 typeDecl = "PFN_%s" % name 353 354 procAddressType = inferProcAddressFuncType(vulkanApi) 355 356 self.cgenHeader.stmt("%s %s" % (typeDecl, name)); 357 self.module.appendHeader(self.cgenHeader.swapCode()) 358 359 current = self.apisToGet.get(procAddressType, []) 360 if current == []: 361 self.apisToGet[procAddressType] = current 362 current.append((vulkanApi, typeDecl, self.currentFeature)) 363 364# VulkanDispatchFast allows one to get the optimal function pointers 365# for a given Vulkan API call, in order to improve performance. 366# 367# We can optionally query VkDevices to get function pointers that are 368# closer to the ICD and have fewer levels of indirection from the loader 369# to get there. 370# See 371# https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md 372# for more info. 373# 374# This requires the calling C++ code to provide functions to 375# generate the desired instances and devices, otherwise we won't know 376# which instance or device to pass to vkGet(Instance|Device)ProcAddr, 377# so it does push more complexity to the user. 378class VulkanDispatchFast(VulkanDispatch): 379 380 def __init__(self, module, typeInfo): 381 VulkanDispatch.__init__(self, module, typeInfo) 382 383 def onBegin(self): 384 self.cgenHeader.line(""" 385void init_vulkan_dispatch_from_system_loader( 386 DlOpenFunc dlOpenFunc, 387 DlSymFunc dlSymFunc, 388 InstanceGetter instanceGetter, 389 DeviceGetter deviceGetter, 390 VulkanDispatch* dispatch_out); 391""") 392 393 self.cgenHeader.line("struct VulkanDispatch {") 394 self.cgenHeader.line("VkInstance instance;") 395 self.cgenHeader.line("VkPhysicalDevice physicalDevice;") 396 self.cgenHeader.line("uint32_t physicalDeviceQueueFamilyInfoCount;") 397 self.cgenHeader.line("VkQueueFamilyProperties* physicalDeviceQueueFamilyInfos;") 398 self.cgenHeader.line("VkDevice device;") 399 self.cgenHeader.line("bool presentCapable;") 400 self.module.appendHeader(self.cgenHeader.swapCode()) 401 402 def makeGetProcAddr(self, cgen, dispatchLevel, dispatch, apiname, typedecl): 403 if dispatchLevel == "instance": 404 funcname = "vkGetInstanceProcAddr" 405 elif dispatchLevel == "device": 406 funcname = "vkGetDeviceProcAddr" 407 else: 408 raise 409 410 cgen.stmt( \ 411 "out->%s = (%s)out->%s(%s, \"%s\")" % \ 412 (apiname, typedecl, funcname, dispatch, apiname)) 413 414 def onEnd(self): 415 self.cgenHeader.line("};") 416 self.module.appendHeader(self.cgenHeader.swapCode()) 417 418 self.cgenImpl.line(""" 419void init_vulkan_dispatch_from_system_loader( 420 DlOpenFunc dlOpenFunc, 421 DlSymFunc dlSymFunc, 422 InstanceGetter instanceGetter, 423 DeviceGetter deviceGetter, 424 VulkanDispatch* out)""") 425 426 self.cgenImpl.beginBlock() 427 428 self.cgenImpl.stmt("out->instance = nullptr") 429 self.cgenImpl.stmt("out->physicalDevice = nullptr") 430 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfoCount = 0") 431 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = nullptr") 432 self.cgenImpl.stmt("out->device = nullptr") 433 self.cgenImpl.stmt("out->presentCapable = false") 434 435 self.cgenImpl.stmt("void* lib = dlOpenFunc()") 436 self.cgenImpl.stmt("if (!lib) return") 437 438 for vulkanApi, typeDecl, feature in self.apisToGet["global"]: 439 self.syncFeature(self.cgenImpl, feature) 440 self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) 441 442 self.syncFeature(self.cgenImpl, "") 443 self.cgenImpl.stmt("if (!out->vkGetInstanceProcAddr) return") 444 445 for vulkanApi, typeDecl, feature in self.apisToGet["global-instance"]: 446 self.syncFeature(self.cgenImpl, feature) 447 self.makeGetProcAddr( \ 448 self.cgenImpl, "instance", "nullptr", vulkanApi.name, typeDecl); 449 450 self.syncFeature(self.cgenImpl, "") 451 self.cgenImpl.stmt("if (!instanceGetter(out, &out->instance)) return") 452 453 for vulkanApi, typeDecl, feature in self.apisToGet["instance"]: 454 self.syncFeature(self.cgenImpl, feature) 455 self.makeGetProcAddr( \ 456 self.cgenImpl, "instance", "out->instance", vulkanApi.name, typeDecl); 457 458 self.syncFeature(self.cgenImpl, "") 459 460 self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, nullptr, &out->device, &out->presentCapable)) return") 461 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = (VkQueueFamilyProperties*)malloc(out->physicalDeviceQueueFamilyInfoCount * sizeof(VkQueueFamilyProperties))"); 462 self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, out->physicalDeviceQueueFamilyInfos, &out->device, &out->presentCapable)) return") 463 464 for vulkanApi, typeDecl, feature in self.apisToGet["device"]: 465 self.syncFeature(self.cgenImpl, feature) 466 self.makeGetProcAddr( \ 467 self.cgenImpl, "device", "out->device", vulkanApi.name, typeDecl); 468 469 self.syncFeature(self.cgenImpl, "") 470 471 self.cgenImpl.endBlock() 472 473 self.module.appendImpl(self.cgenImpl.swapCode()) 474 475 def onBeginFeature(self, featureName, featureType): 476 VulkanDispatch.onBeginFeature(self, featureName, featureType); 477 478 def onGenType(self, typeXml, name, alias): 479 VulkanDispatch.onGenType(self, typeXml, name, alias); 480 481 def onGenCmd(self, cmdinfo, name, alias): 482 VulkanDispatch.onGenCmd(self, cmdinfo, name, alias); 483