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