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 self.apisToGet["global-only"] 263 264 for vulkanApi, typeDecl, feature in apis: 265 self.syncFeature(self.cgenImpl, feature) 266 self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) 267 268 self.syncFeature(self.cgenImpl, "") 269 self.cgenImpl.endBlock() 270 271 # Getting instance dispatch tables 272 self.cgenImpl.line(""" 273void init_vulkan_dispatch_from_instance( 274 VulkanDispatch* vk, 275 VkInstance instance, 276 VulkanDispatch* out)""") 277 278 self.cgenImpl.beginBlock() 279 280 self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") 281 282 apis = \ 283 self.apisToGet["global"] + \ 284 self.apisToGet["global-instance"] + \ 285 self.apisToGet["instance"] + \ 286 self.apisToGet["device"] 287 288 for vulkanApi, typeDecl, feature in apis: 289 self.syncFeature(self.cgenImpl, feature) 290 self.makeGetInstanceProcAddrCall( 291 self.cgenImpl, "vk", "instance", vulkanApi.name, typeDecl) 292 293 self.syncFeature(self.cgenImpl, "") 294 self.cgenImpl.endBlock() 295 296 # Getting device dispatch tables 297 self.cgenImpl.line(""" 298void init_vulkan_dispatch_from_device( 299 VulkanDispatch* vk, 300 VkDevice device, 301 VulkanDispatch* out)""") 302 303 self.cgenImpl.beginBlock() 304 305 self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") 306 307 apis = \ 308 self.apisToGet["global"] + \ 309 self.apisToGet["global-instance"] + \ 310 self.apisToGet["instance"] + \ 311 self.apisToGet["device"] 312 313 for vulkanApi, typeDecl, feature in apis: 314 self.syncFeature(self.cgenImpl, feature) 315 self.makeGetDeviceProcAddrCall( 316 self.cgenImpl, "vk", "device", vulkanApi.name, typeDecl) 317 318 self.syncFeature(self.cgenImpl, "") 319 self.cgenImpl.endBlock() 320 321 # Check Vulkan 1.0 / 1.1 functions 322 323 def emit_check_impl(cgen, dispatchVar, feature, featureToCheck, apiName): 324 if feature == featureToCheck: 325 cgen.beginIf("!%s->%s" % (dispatchVar, apiName)) 326 cgen.stmt("fprintf(stderr, \"%s check failed: %s not found\\n\")" % (featureToCheck, apiName)) 327 cgen.stmt("good = false") 328 cgen.endIf() 329 330 def emit_feature_check_impl(context, cgen, tag, featureToCheck, apis): 331 cgen.line(""" 332bool vulkan_dispatch_check_%s_%s( 333 const VulkanDispatch* vk) 334""" % (tag, featureToCheck)) 335 336 cgen.beginBlock() 337 338 cgen.stmt("bool good = true") 339 340 for vulkanApi, typeDecl, feature in apis: 341 context.syncFeatureQuiet(self.cgenImpl, feature) 342 emit_check_impl(cgen, "vk", feature, featureToCheck, vulkanApi.name) 343 344 context.syncFeatureQuiet(self.cgenImpl, "") 345 346 cgen.stmt("return good") 347 cgen.endBlock() 348 349 instanceApis = self.apisToGet["global-instance"] + self.apisToGet["instance"] 350 351 emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_0", instanceApis) 352 emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_1", instanceApis) 353 emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_0", self.apisToGet["device"]) 354 emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_1", self.apisToGet["device"]) 355 356 self.module.appendImpl(self.cgenImpl.swapCode()) 357 358 def onBeginFeature(self, featureName, featureType): 359 self.currentFeature = featureName 360 361 def onGenType(self, typeXml, name, alias): 362 VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) 363 364 def onGenCmd(self, cmdinfo, name, alias): 365 VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) 366 367 vulkanApi = self.typeInfo.apis[name] 368 369 typeDecl = "PFN_%s" % name 370 371 procAddressType = inferProcAddressFuncType(vulkanApi) 372 373 self.cgenHeader.stmt("%s %s" % (typeDecl, name)); 374 self.module.appendHeader(self.cgenHeader.swapCode()) 375 376 current = self.apisToGet.get(procAddressType, []) 377 if current == []: 378 self.apisToGet[procAddressType] = current 379 current.append((vulkanApi, typeDecl, self.currentFeature)) 380 381# VulkanDispatchFast allows one to get the optimal function pointers 382# for a given Vulkan API call, in order to improve performance. 383# 384# We can optionally query VkDevices to get function pointers that are 385# closer to the ICD and have fewer levels of indirection from the loader 386# to get there. 387# See 388# https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md 389# for more info. 390# 391# This requires the calling C++ code to provide functions to 392# generate the desired instances and devices, otherwise we won't know 393# which instance or device to pass to vkGet(Instance|Device)ProcAddr, 394# so it does push more complexity to the user. 395class VulkanDispatchFast(VulkanDispatch): 396 397 def __init__(self, module, typeInfo): 398 VulkanDispatch.__init__(self, module, typeInfo) 399 400 def onBegin(self): 401 self.cgenHeader.line(""" 402void init_vulkan_dispatch_from_system_loader( 403 DlOpenFunc dlOpenFunc, 404 DlSymFunc dlSymFunc, 405 InstanceGetter instanceGetter, 406 DeviceGetter deviceGetter, 407 VulkanDispatch* dispatch_out); 408""") 409 410 self.cgenHeader.line("struct VulkanDispatch {") 411 self.cgenHeader.line("VkInstance instance;") 412 self.cgenHeader.line("VkPhysicalDevice physicalDevice;") 413 self.cgenHeader.line("uint32_t physicalDeviceQueueFamilyInfoCount;") 414 self.cgenHeader.line("VkQueueFamilyProperties* physicalDeviceQueueFamilyInfos;") 415 self.cgenHeader.line("VkDevice device;") 416 self.cgenHeader.line("bool presentCapable;") 417 self.module.appendHeader(self.cgenHeader.swapCode()) 418 419 def makeGetProcAddr(self, cgen, dispatchLevel, dispatch, apiname, typedecl): 420 if dispatchLevel == "instance": 421 funcname = "vkGetInstanceProcAddr" 422 elif dispatchLevel == "device": 423 funcname = "vkGetDeviceProcAddr" 424 else: 425 raise 426 427 cgen.stmt( \ 428 "out->%s = (%s)out->%s(%s, \"%s\")" % \ 429 (apiname, typedecl, funcname, dispatch, apiname)) 430 431 def onEnd(self): 432 self.cgenHeader.line("};") 433 self.module.appendHeader(self.cgenHeader.swapCode()) 434 435 self.cgenImpl.line(""" 436void init_vulkan_dispatch_from_system_loader( 437 DlOpenFunc dlOpenFunc, 438 DlSymFunc dlSymFunc, 439 InstanceGetter instanceGetter, 440 DeviceGetter deviceGetter, 441 VulkanDispatch* out)""") 442 443 self.cgenImpl.beginBlock() 444 445 self.cgenImpl.stmt("out->instance = nullptr") 446 self.cgenImpl.stmt("out->physicalDevice = nullptr") 447 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfoCount = 0") 448 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = nullptr") 449 self.cgenImpl.stmt("out->device = nullptr") 450 self.cgenImpl.stmt("out->presentCapable = false") 451 452 self.cgenImpl.stmt("void* lib = dlOpenFunc()") 453 self.cgenImpl.stmt("if (!lib) return") 454 455 for vulkanApi, typeDecl, feature in self.apisToGet["global"]: 456 self.syncFeature(self.cgenImpl, feature) 457 self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) 458 459 self.syncFeature(self.cgenImpl, "") 460 self.cgenImpl.stmt("if (!out->vkGetInstanceProcAddr) return") 461 462 for vulkanApi, typeDecl, feature in self.apisToGet["global-instance"]: 463 self.syncFeature(self.cgenImpl, feature) 464 self.makeGetProcAddr( \ 465 self.cgenImpl, "instance", "nullptr", vulkanApi.name, typeDecl); 466 467 self.syncFeature(self.cgenImpl, "") 468 self.cgenImpl.stmt("if (!instanceGetter(out, &out->instance)) return") 469 470 for vulkanApi, typeDecl, feature in self.apisToGet["instance"]: 471 self.syncFeature(self.cgenImpl, feature) 472 self.makeGetProcAddr( \ 473 self.cgenImpl, "instance", "out->instance", vulkanApi.name, typeDecl); 474 475 self.syncFeature(self.cgenImpl, "") 476 477 self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, nullptr, &out->device, &out->presentCapable)) return") 478 self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = (VkQueueFamilyProperties*)malloc(out->physicalDeviceQueueFamilyInfoCount * sizeof(VkQueueFamilyProperties))"); 479 self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, out->physicalDeviceQueueFamilyInfos, &out->device, &out->presentCapable)) return") 480 481 for vulkanApi, typeDecl, feature in self.apisToGet["device"]: 482 self.syncFeature(self.cgenImpl, feature) 483 self.makeGetProcAddr( \ 484 self.cgenImpl, "device", "out->device", vulkanApi.name, typeDecl); 485 486 self.syncFeature(self.cgenImpl, "") 487 488 self.cgenImpl.endBlock() 489 490 self.module.appendImpl(self.cgenImpl.swapCode()) 491 492 def onBeginFeature(self, featureName, featureType): 493 VulkanDispatch.onBeginFeature(self, featureName, featureType); 494 495 def onGenType(self, typeXml, name, alias): 496 VulkanDispatch.onGenType(self, typeXml, name, alias); 497 498 def onGenCmd(self, cmdinfo, name, alias): 499 VulkanDispatch.onGenCmd(self, cmdinfo, name, alias); 500