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