• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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