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