500 lines
18 KiB
Python
500 lines
18 KiB
Python
# Copyright (c) 2018 The Android Open Source Project
|
|
# Copyright (c) 2018 Google Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from .common.codegen import CodeGen
|
|
from .common.vulkantypes import \
|
|
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
|
|
|
|
from .wrapperdefs import VulkanWrapperGenerator
|
|
|
|
# No real good way to automatically infer the most important Vulkan API
|
|
# functions as it relates to which getProcAddress function to use, plus
|
|
# we might want to control which function to use depending on our
|
|
# performance needs.
|
|
|
|
# This is based on the minimum set of functions needed to be directly
|
|
# queried with dlsym and not returning null.
|
|
getProcAddrFuncs = [
|
|
"vkGetInstanceProcAddr",
|
|
"vkDestroyInstance",
|
|
"vkEnumeratePhysicalDevices",
|
|
"vkGetPhysicalDeviceFeatures",
|
|
"vkGetPhysicalDeviceFormatProperties",
|
|
"vkGetPhysicalDeviceImageFormatProperties",
|
|
"vkGetPhysicalDeviceProperties",
|
|
"vkGetPhysicalDeviceQueueFamilyProperties",
|
|
"vkGetPhysicalDeviceMemoryProperties",
|
|
"vkCreateDevice",
|
|
"vkDestroyDevice",
|
|
"vkEnumerateDeviceExtensionProperties",
|
|
"vkEnumerateDeviceLayerProperties",
|
|
]
|
|
|
|
# Some methods can only be found using dlsym() while we cannot get the function
|
|
# address using vkGetInstProcAddr() or vkGetDeviceProcAddr(). These function
|
|
# pointers should only be initialized when setting up the dispatch from system
|
|
# loader.
|
|
getProcAddrOnlyFuncs = [
|
|
"vkGetMTLDeviceMVK",
|
|
"vkSetMTLTextureMVK",
|
|
"vkGetMTLTextureMVK",
|
|
"vkGetMTLBufferMVK",
|
|
"vkUseIOSurfaceMVK",
|
|
"vkGetIOSurfaceMVK",
|
|
]
|
|
|
|
getInstanceProcAddrNoInstanceFuncs = [
|
|
"vkCreateInstance",
|
|
"vkEnumerateInstanceExtensionProperties",
|
|
"vkEnumerateInstanceLayerProperties",
|
|
]
|
|
|
|
getInstanceProcAddrFuncs = [
|
|
"vkGetDeviceProcAddr",
|
|
"vkCreateSwapchainKHR",
|
|
"vkDestroySwapchainKHR",
|
|
"vkGetSwapchainImagesKHR",
|
|
"vkAcquireNextImageKHR",
|
|
"vkQueuePresentKHR",
|
|
"vkCreateMacOSSurfaceMVK",
|
|
"vkCreateWin32SurfaceKHR",
|
|
"vkGetPhysicalDeviceWin32PresentationSupportKHR",
|
|
"vkCreateXlibSurfaceKHR",
|
|
"vkGetPhysicalDeviceXlibPresentationSupportKHR",
|
|
"vkCreateXcbSurfaceKHR",
|
|
"vkGetPhysicalDeviceXcbPresentationSupportKHR",
|
|
"vkGetPhysicalDeviceSparseImageFormatProperties",
|
|
"vkEnumerateInstanceVersion",
|
|
"vkEnumeratePhysicalDeviceGroups",
|
|
"vkGetPhysicalDeviceFeatures2",
|
|
"vkGetPhysicalDeviceProperties2",
|
|
"vkGetPhysicalDeviceFormatProperties2",
|
|
"vkGetPhysicalDeviceImageFormatProperties2",
|
|
"vkGetPhysicalDeviceQueueFamilyProperties2",
|
|
"vkGetPhysicalDeviceMemoryProperties2",
|
|
"vkGetPhysicalDeviceSparseImageFormatProperties2",
|
|
"vkGetPhysicalDeviceExternalBufferProperties",
|
|
"vkGetPhysicalDeviceExternalFenceProperties",
|
|
"vkGetPhysicalDeviceExternalSemaphoreProperties",
|
|
]
|
|
|
|
# Implicitly, everything else is going to be obtained
|
|
# with vkGetDeviceProcAddr,
|
|
# unless it has instance in the arg.
|
|
|
|
def isGetProcAddressAPI(vulkanApi):
|
|
return vulkanApi.name in getProcAddrFuncs
|
|
|
|
def isGetProcAddressOnlyAPI(vulkanApi):
|
|
return vulkanApi.name in getProcAddrOnlyFuncs
|
|
|
|
def isGetInstanceProcAddressNoInstanceAPI(vulkanApi):
|
|
return vulkanApi.name in getInstanceProcAddrNoInstanceFuncs
|
|
|
|
def isGetInstanceProcAddressAPI(vulkanApi):
|
|
if vulkanApi.name in getInstanceProcAddrFuncs:
|
|
return True
|
|
|
|
if vulkanApi.parameters[0].typeName == "VkInstance":
|
|
return True
|
|
|
|
return False
|
|
|
|
def isGetDeviceProcAddressAPI(vulkanApi):
|
|
if isGetProcAddressAPI(vulkanApi):
|
|
return False
|
|
|
|
if isGetProcAddressOnlyAPI(vulkanApi):
|
|
return False
|
|
|
|
if isGetInstanceProcAddressAPI(vulkanApi):
|
|
return False
|
|
|
|
return True
|
|
|
|
def inferProcAddressFuncType(vulkanApi):
|
|
if isGetProcAddressAPI(vulkanApi):
|
|
return "global"
|
|
if isGetProcAddressOnlyAPI(vulkanApi):
|
|
return "global-only"
|
|
if isGetInstanceProcAddressNoInstanceAPI(vulkanApi):
|
|
return "global-instance"
|
|
if isGetInstanceProcAddressAPI(vulkanApi):
|
|
return "instance"
|
|
return "device"
|
|
|
|
# VulkanDispatch defines a struct, VulkanDispatch,
|
|
# that is populated by function pointers from the Vulkan
|
|
# loader. No attempt is made to do something different
|
|
# for instance vs device functions.
|
|
class VulkanDispatch(VulkanWrapperGenerator):
|
|
def __init__(self, module, typeInfo):
|
|
VulkanWrapperGenerator.__init__(self, module, typeInfo)
|
|
|
|
self.apisToGet = {}
|
|
|
|
self.cgenHeader = CodeGen()
|
|
self.cgenImpl = CodeGen()
|
|
self.typeInfo = typeInfo
|
|
|
|
self.currentFeature = ""
|
|
self.featureForCodegen = ""
|
|
|
|
def onBegin(self):
|
|
|
|
# The first way is to use just the loader to get symbols. This doesn't
|
|
# necessarily work with extensions because at that point the dispatch
|
|
# table needs to be specific to a particular Vulkan instance or device.
|
|
|
|
self.cgenHeader.line("""
|
|
void init_vulkan_dispatch_from_system_loader(
|
|
DlOpenFunc dlOpenFunc,
|
|
DlSymFunc dlSymFunc,
|
|
VulkanDispatch* dispatch_out);
|
|
""")
|
|
|
|
# The second way is to initialize the table from a given Vulkan
|
|
# instance or device. Provided the instance or device was created with
|
|
# the right extensions, we can obtain function pointers to extension
|
|
# functions this way.
|
|
|
|
self.cgenHeader.line("""
|
|
void init_vulkan_dispatch_from_instance(
|
|
VulkanDispatch* vk,
|
|
VkInstance instance,
|
|
VulkanDispatch* dispatch_out);
|
|
""")
|
|
self.cgenHeader.line("""
|
|
void init_vulkan_dispatch_from_device(
|
|
VulkanDispatch* vk,
|
|
VkDevice device,
|
|
VulkanDispatch* dispatch_out);
|
|
""")
|
|
|
|
# After populating a VulkanDispatch with the above methods,
|
|
# it can be useful to check whether the Vulkan 1.0 or 1.1 methods
|
|
# are all there.
|
|
def emit_feature_check_decl(cgen, tag, featureToCheck):
|
|
cgen.line("""
|
|
bool vulkan_dispatch_check_%s_%s(
|
|
const VulkanDispatch* vk);
|
|
""" % (tag, featureToCheck))
|
|
|
|
emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_0")
|
|
emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_1")
|
|
emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_0")
|
|
emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_1")
|
|
|
|
self.cgenHeader.line("struct VulkanDispatch {")
|
|
self.module.appendHeader(self.cgenHeader.swapCode())
|
|
|
|
def syncFeatureQuiet(self, cgen, feature):
|
|
if self.featureForCodegen != feature:
|
|
if feature == "":
|
|
self.featureForCodegen = feature
|
|
return
|
|
|
|
self.featureForCodegen = feature
|
|
|
|
def syncFeature(self, cgen, feature):
|
|
if self.featureForCodegen != feature:
|
|
if feature == "":
|
|
cgen.leftline("#endif")
|
|
self.featureForCodegen = feature
|
|
return
|
|
|
|
if self.featureForCodegen != "":
|
|
cgen.leftline("#endif")
|
|
|
|
cgen.leftline("#ifdef %s" % feature)
|
|
self.featureForCodegen = feature
|
|
|
|
def makeDlsymCall(self, cgen, apiname, typedecl):
|
|
cgen.stmt( \
|
|
"out->%s = (%s)dlSymFunc(lib, \"%s\")" % \
|
|
(apiname, typedecl, apiname))
|
|
|
|
def makeGetInstanceProcAddrCall(self, cgen, dispatch, instance, apiname, typedecl):
|
|
cgen.stmt( \
|
|
"out->%s = (%s)%s->vkGetInstanceProcAddr(%s, \"%s\")" % \
|
|
(apiname, typedecl, dispatch, instance, apiname))
|
|
|
|
def makeGetDeviceProcAddrCall(self, cgen, dispatch, device, apiname, typedecl):
|
|
cgen.stmt( \
|
|
"out->%s = (%s)%s->vkGetDeviceProcAddr(%s, \"%s\")" % \
|
|
(apiname, typedecl, dispatch, device, apiname))
|
|
|
|
def onEnd(self):
|
|
self.cgenHeader.line("};")
|
|
self.module.appendHeader(self.cgenHeader.swapCode())
|
|
|
|
# Getting dispatch tables from the loader
|
|
self.cgenImpl.line("""
|
|
void init_vulkan_dispatch_from_system_loader(
|
|
DlOpenFunc dlOpenFunc,
|
|
DlSymFunc dlSymFunc,
|
|
VulkanDispatch* out)""")
|
|
|
|
self.cgenImpl.beginBlock()
|
|
|
|
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
|
|
|
|
self.cgenImpl.stmt("void* lib = dlOpenFunc()")
|
|
self.cgenImpl.stmt("if (!lib) return")
|
|
|
|
apis = \
|
|
self.apisToGet["global"] + \
|
|
self.apisToGet["global-instance"] + \
|
|
self.apisToGet["instance"] + \
|
|
self.apisToGet["device"] + \
|
|
self.apisToGet["global-only"]
|
|
|
|
for vulkanApi, typeDecl, feature in apis:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl)
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
self.cgenImpl.endBlock()
|
|
|
|
# Getting instance dispatch tables
|
|
self.cgenImpl.line("""
|
|
void init_vulkan_dispatch_from_instance(
|
|
VulkanDispatch* vk,
|
|
VkInstance instance,
|
|
VulkanDispatch* out)""")
|
|
|
|
self.cgenImpl.beginBlock()
|
|
|
|
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
|
|
|
|
apis = \
|
|
self.apisToGet["global"] + \
|
|
self.apisToGet["global-instance"] + \
|
|
self.apisToGet["instance"] + \
|
|
self.apisToGet["device"]
|
|
|
|
for vulkanApi, typeDecl, feature in apis:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeGetInstanceProcAddrCall(
|
|
self.cgenImpl, "vk", "instance", vulkanApi.name, typeDecl)
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
self.cgenImpl.endBlock()
|
|
|
|
# Getting device dispatch tables
|
|
self.cgenImpl.line("""
|
|
void init_vulkan_dispatch_from_device(
|
|
VulkanDispatch* vk,
|
|
VkDevice device,
|
|
VulkanDispatch* out)""")
|
|
|
|
self.cgenImpl.beginBlock()
|
|
|
|
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
|
|
|
|
apis = \
|
|
self.apisToGet["global"] + \
|
|
self.apisToGet["global-instance"] + \
|
|
self.apisToGet["instance"] + \
|
|
self.apisToGet["device"]
|
|
|
|
for vulkanApi, typeDecl, feature in apis:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeGetDeviceProcAddrCall(
|
|
self.cgenImpl, "vk", "device", vulkanApi.name, typeDecl)
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
self.cgenImpl.endBlock()
|
|
|
|
# Check Vulkan 1.0 / 1.1 functions
|
|
|
|
def emit_check_impl(cgen, dispatchVar, feature, featureToCheck, apiName):
|
|
if feature == featureToCheck:
|
|
cgen.beginIf("!%s->%s" % (dispatchVar, apiName))
|
|
cgen.stmt("fprintf(stderr, \"%s check failed: %s not found\\n\")" % (featureToCheck, apiName))
|
|
cgen.stmt("good = false")
|
|
cgen.endIf()
|
|
|
|
def emit_feature_check_impl(context, cgen, tag, featureToCheck, apis):
|
|
cgen.line("""
|
|
bool vulkan_dispatch_check_%s_%s(
|
|
const VulkanDispatch* vk)
|
|
""" % (tag, featureToCheck))
|
|
|
|
cgen.beginBlock()
|
|
|
|
cgen.stmt("bool good = true")
|
|
|
|
for vulkanApi, typeDecl, feature in apis:
|
|
context.syncFeatureQuiet(self.cgenImpl, feature)
|
|
emit_check_impl(cgen, "vk", feature, featureToCheck, vulkanApi.name)
|
|
|
|
context.syncFeatureQuiet(self.cgenImpl, "")
|
|
|
|
cgen.stmt("return good")
|
|
cgen.endBlock()
|
|
|
|
instanceApis = self.apisToGet["global-instance"] + self.apisToGet["instance"]
|
|
|
|
emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_0", instanceApis)
|
|
emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_1", instanceApis)
|
|
emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_0", self.apisToGet["device"])
|
|
emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_1", self.apisToGet["device"])
|
|
|
|
self.module.appendImpl(self.cgenImpl.swapCode())
|
|
|
|
def onBeginFeature(self, featureName, featureType):
|
|
self.currentFeature = featureName
|
|
|
|
def onGenType(self, typeXml, name, alias):
|
|
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
|
|
|
|
def onGenCmd(self, cmdinfo, name, alias):
|
|
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
|
|
|
|
vulkanApi = self.typeInfo.apis[name]
|
|
|
|
typeDecl = "PFN_%s" % name
|
|
|
|
procAddressType = inferProcAddressFuncType(vulkanApi)
|
|
|
|
self.cgenHeader.stmt("%s %s" % (typeDecl, name));
|
|
self.module.appendHeader(self.cgenHeader.swapCode())
|
|
|
|
current = self.apisToGet.get(procAddressType, [])
|
|
if current == []:
|
|
self.apisToGet[procAddressType] = current
|
|
current.append((vulkanApi, typeDecl, self.currentFeature))
|
|
|
|
# VulkanDispatchFast allows one to get the optimal function pointers
|
|
# for a given Vulkan API call, in order to improve performance.
|
|
#
|
|
# We can optionally query VkDevices to get function pointers that are
|
|
# closer to the ICD and have fewer levels of indirection from the loader
|
|
# to get there.
|
|
# See
|
|
# https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md
|
|
# for more info.
|
|
#
|
|
# This requires the calling C++ code to provide functions to
|
|
# generate the desired instances and devices, otherwise we won't know
|
|
# which instance or device to pass to vkGet(Instance|Device)ProcAddr,
|
|
# so it does push more complexity to the user.
|
|
class VulkanDispatchFast(VulkanDispatch):
|
|
|
|
def __init__(self, module, typeInfo):
|
|
VulkanDispatch.__init__(self, module, typeInfo)
|
|
|
|
def onBegin(self):
|
|
self.cgenHeader.line("""
|
|
void init_vulkan_dispatch_from_system_loader(
|
|
DlOpenFunc dlOpenFunc,
|
|
DlSymFunc dlSymFunc,
|
|
InstanceGetter instanceGetter,
|
|
DeviceGetter deviceGetter,
|
|
VulkanDispatch* dispatch_out);
|
|
""")
|
|
|
|
self.cgenHeader.line("struct VulkanDispatch {")
|
|
self.cgenHeader.line("VkInstance instance;")
|
|
self.cgenHeader.line("VkPhysicalDevice physicalDevice;")
|
|
self.cgenHeader.line("uint32_t physicalDeviceQueueFamilyInfoCount;")
|
|
self.cgenHeader.line("VkQueueFamilyProperties* physicalDeviceQueueFamilyInfos;")
|
|
self.cgenHeader.line("VkDevice device;")
|
|
self.cgenHeader.line("bool presentCapable;")
|
|
self.module.appendHeader(self.cgenHeader.swapCode())
|
|
|
|
def makeGetProcAddr(self, cgen, dispatchLevel, dispatch, apiname, typedecl):
|
|
if dispatchLevel == "instance":
|
|
funcname = "vkGetInstanceProcAddr"
|
|
elif dispatchLevel == "device":
|
|
funcname = "vkGetDeviceProcAddr"
|
|
else:
|
|
raise
|
|
|
|
cgen.stmt( \
|
|
"out->%s = (%s)out->%s(%s, \"%s\")" % \
|
|
(apiname, typedecl, funcname, dispatch, apiname))
|
|
|
|
def onEnd(self):
|
|
self.cgenHeader.line("};")
|
|
self.module.appendHeader(self.cgenHeader.swapCode())
|
|
|
|
self.cgenImpl.line("""
|
|
void init_vulkan_dispatch_from_system_loader(
|
|
DlOpenFunc dlOpenFunc,
|
|
DlSymFunc dlSymFunc,
|
|
InstanceGetter instanceGetter,
|
|
DeviceGetter deviceGetter,
|
|
VulkanDispatch* out)""")
|
|
|
|
self.cgenImpl.beginBlock()
|
|
|
|
self.cgenImpl.stmt("out->instance = nullptr")
|
|
self.cgenImpl.stmt("out->physicalDevice = nullptr")
|
|
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfoCount = 0")
|
|
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = nullptr")
|
|
self.cgenImpl.stmt("out->device = nullptr")
|
|
self.cgenImpl.stmt("out->presentCapable = false")
|
|
|
|
self.cgenImpl.stmt("void* lib = dlOpenFunc()")
|
|
self.cgenImpl.stmt("if (!lib) return")
|
|
|
|
for vulkanApi, typeDecl, feature in self.apisToGet["global"]:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl)
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
self.cgenImpl.stmt("if (!out->vkGetInstanceProcAddr) return")
|
|
|
|
for vulkanApi, typeDecl, feature in self.apisToGet["global-instance"]:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeGetProcAddr( \
|
|
self.cgenImpl, "instance", "nullptr", vulkanApi.name, typeDecl);
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
self.cgenImpl.stmt("if (!instanceGetter(out, &out->instance)) return")
|
|
|
|
for vulkanApi, typeDecl, feature in self.apisToGet["instance"]:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeGetProcAddr( \
|
|
self.cgenImpl, "instance", "out->instance", vulkanApi.name, typeDecl);
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
|
|
self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, nullptr, &out->device, &out->presentCapable)) return")
|
|
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = (VkQueueFamilyProperties*)malloc(out->physicalDeviceQueueFamilyInfoCount * sizeof(VkQueueFamilyProperties))");
|
|
self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, out->physicalDeviceQueueFamilyInfos, &out->device, &out->presentCapable)) return")
|
|
|
|
for vulkanApi, typeDecl, feature in self.apisToGet["device"]:
|
|
self.syncFeature(self.cgenImpl, feature)
|
|
self.makeGetProcAddr( \
|
|
self.cgenImpl, "device", "out->device", vulkanApi.name, typeDecl);
|
|
|
|
self.syncFeature(self.cgenImpl, "")
|
|
|
|
self.cgenImpl.endBlock()
|
|
|
|
self.module.appendImpl(self.cgenImpl.swapCode())
|
|
|
|
def onBeginFeature(self, featureName, featureType):
|
|
VulkanDispatch.onBeginFeature(self, featureName, featureType);
|
|
|
|
def onGenType(self, typeXml, name, alias):
|
|
VulkanDispatch.onGenType(self, typeXml, name, alias);
|
|
|
|
def onGenCmd(self, cmdinfo, name, alias):
|
|
VulkanDispatch.onGenCmd(self, cmdinfo, name, alias);
|