• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from .common.codegen import CodeGen, VulkanWrapperGenerator
2from .common.vulkantypes import \
3        VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
4from .common.vulkantypes import EXCLUDED_APIS
5from .common.vulkantypes import HANDLE_TYPES
6
7import copy
8import re
9
10RESOURCE_TRACKER_ENTRIES = [
11    "vkEnumerateInstanceExtensionProperties",
12    "vkEnumerateDeviceExtensionProperties",
13    "vkEnumeratePhysicalDevices",
14    "vkAllocateMemory",
15    "vkFreeMemory",
16    "vkCreateImage",
17    "vkDestroyImage",
18    "vkGetImageMemoryRequirements",
19    "vkGetImageMemoryRequirements2",
20    "vkGetImageMemoryRequirements2KHR",
21    "vkGetImageSubresourceLayout",
22    "vkBindImageMemory",
23    "vkBindImageMemory2",
24    "vkBindImageMemory2KHR",
25    "vkCreateBuffer",
26    "vkDestroyBuffer",
27    "vkGetBufferMemoryRequirements",
28    "vkGetBufferMemoryRequirements2",
29    "vkGetBufferMemoryRequirements2KHR",
30    "vkBindBufferMemory",
31    "vkBindBufferMemory2",
32    "vkBindBufferMemory2KHR",
33    "vkCreateSemaphore",
34    "vkDestroySemaphore",
35    "vkQueueSubmit",
36    "vkQueueSubmit2",
37    "vkQueueWaitIdle",
38    "vkImportSemaphoreFdKHR",
39    "vkGetSemaphoreFdKHR",
40    # Warning: These need to be defined in vk.xml (currently no-op) {
41    "vkGetMemoryFuchsiaHandleKHR",
42    "vkGetMemoryFuchsiaHandlePropertiesKHR",
43    "vkGetSemaphoreFuchsiaHandleKHR",
44    "vkImportSemaphoreFuchsiaHandleKHR",
45    # } end Warning: These need to be defined in vk.xml (currently no-op)
46    "vkGetAndroidHardwareBufferPropertiesANDROID",
47    "vkGetMemoryAndroidHardwareBufferANDROID",
48    "vkGetMemoryFdKHR",
49    "vkGetMemoryFdPropertiesKHR",
50    "vkCreateSamplerYcbcrConversion",
51    "vkDestroySamplerYcbcrConversion",
52    "vkCreateSamplerYcbcrConversionKHR",
53    "vkDestroySamplerYcbcrConversionKHR",
54    "vkUpdateDescriptorSetWithTemplate",
55    "vkUpdateDescriptorSetWithTemplateKHR",
56    "vkGetPhysicalDeviceImageFormatProperties2",
57    "vkGetPhysicalDeviceImageFormatProperties2KHR",
58    "vkBeginCommandBuffer",
59    "vkEndCommandBuffer",
60    "vkResetCommandBuffer",
61    "vkCreateImageView",
62    "vkCreateSampler",
63    "vkGetPhysicalDeviceExternalFenceProperties",
64    "vkGetPhysicalDeviceExternalFencePropertiesKHR",
65    "vkGetPhysicalDeviceExternalBufferProperties",
66    "vkGetPhysicalDeviceExternalBufferPropertiesKHR",
67    "vkCreateFence",
68    "vkResetFences",
69    "vkImportFenceFdKHR",
70    "vkGetFenceFdKHR",
71    "vkWaitForFences",
72    "vkCreateDescriptorPool",
73    "vkDestroyDescriptorPool",
74    "vkResetDescriptorPool",
75    "vkAllocateDescriptorSets",
76    "vkFreeDescriptorSets",
77    "vkCreateDescriptorSetLayout",
78    "vkCmdExecuteCommands",
79    "vkCmdBindDescriptorSets",
80    "vkDestroyDescriptorSetLayout",
81    "vkAllocateCommandBuffers",
82    "vkQueueSignalReleaseImageANDROID",
83    "vkCmdPipelineBarrier",
84    "vkCreateGraphicsPipelines",
85    # Fuchsia
86    "vkGetMemoryZirconHandleFUCHSIA",
87    "vkGetMemoryZirconHandlePropertiesFUCHSIA",
88    "vkGetSemaphoreZirconHandleFUCHSIA",
89    "vkImportSemaphoreZirconHandleFUCHSIA",
90    "vkCreateBufferCollectionFUCHSIA",
91    "vkDestroyBufferCollectionFUCHSIA",
92    "vkSetBufferCollectionImageConstraintsFUCHSIA",
93    "vkSetBufferCollectionBufferConstraintsFUCHSIA",
94    "vkGetBufferCollectionPropertiesFUCHSIA",
95]
96
97SUCCESS_VAL = {
98    "VkResult" : ["VK_SUCCESS"],
99}
100
101HANDWRITTEN_ENTRY_POINTS = [
102    # Instance/device/physical-device special-handling, dispatch tables, etc..
103    "vkCreateInstance",
104    "vkDestroyInstance",
105    "vkGetInstanceProcAddr",
106    "vkEnumerateInstanceVersion",
107    "vkEnumerateInstanceLayerProperties",
108    "vkEnumerateInstanceExtensionProperties",
109    "vkEnumerateDeviceExtensionProperties",
110    "vkGetDeviceProcAddr",
111    "vkEnumeratePhysicalDevices",
112    "vkEnumeratePhysicalDeviceGroups",
113    "vkCreateDevice",
114    "vkDestroyDevice",
115    # Manual alloc/free + vk_*_init/free() call w/ special params
116    "vkGetDeviceQueue",
117    "vkGetDeviceQueue2",
118    # Command pool/buffer handling
119    "vkCreateCommandPool",
120    "vkDestroyCommandPool",
121    "vkAllocateCommandBuffers",
122    "vkResetCommandPool",
123    "vkFreeCommandBuffers",
124    "vkResetCommandPool",
125    # Special cases to handle struct translations in the pNext chain
126    # TODO: Make a codegen module (use deepcopy as reference) to make this more robust
127    "vkAllocateMemory",
128    "vkUpdateDescriptorSets",
129]
130
131# Handles that need to be translated to/from their corresponding gfxstream object types
132HANDLES_TRANSLATE = {
133    "VkInstance",
134    "VkPhysicalDevice",
135    "VkDevice",
136    "VkQueue",
137    "VkCommandPool",
138    "VkCommandBuffer",
139    "VkFence",
140    "VkSemaphore",
141    # TODO: Still need this translation to avoid descriptorSets crash
142    "VkBuffer",
143}
144
145# Types that have a corresponding method for transforming
146# an input list to its internal counterpart
147TYPES_TRANSFORM_LIST_METHOD = {
148    "VkFence",
149    "VkSemaphore",
150    "VkSemaphoreSubmitInfo",
151}
152
153def is_cmdbuf_dispatch(api):
154    return "VkCommandBuffer" == api.parameters[0].typeName
155
156def is_queue_dispatch(api):
157    return "VkQueue" == api.parameters[0].typeName
158
159def getCreateParam(api):
160    for param in api.parameters:
161        if param.isCreatedBy(api):
162            return param
163    return None
164
165def getDestroyParam(api):
166    for param in api.parameters:
167        if param.isDestroyedBy(api):
168            return param
169    return None
170
171# i.e. VkQueryPool --> vk_query_pool
172def typeNameToMesaType(typeName):
173    vkTypeNameRegex = "(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"
174    words = re.split(vkTypeNameRegex, typeName)
175    outputType = "vk"
176    for word in words[1:]:
177        outputType += "_"
178        outputType += word.lower()
179    return outputType
180
181def typeNameToBaseName(typeName):
182    return typeNameToMesaType(typeName)[len("vk_"):]
183
184def paramNameToObjectName(paramName):
185    return "gfxstream_%s" % paramName
186
187def typeNameToVkObjectType(typeName):
188    return "VK_OBJECT_TYPE_%s" % typeNameToBaseName(typeName).upper()
189
190def typeNameToObjectType(typeName):
191    return "gfxstream_vk_%s" % typeNameToBaseName(typeName)
192
193def transformListFuncName(typeName):
194    return "transform%sList" % (typeName)
195
196def isAllocatorParam(param):
197    ALLOCATOR_TYPE_NAME = "VkAllocationCallbacks"
198    return (param.pointerIndirectionLevels == 1
199            and param.isConst
200            and param.typeName == ALLOCATOR_TYPE_NAME)
201
202def isArrayParam(param):
203    return (1 == param.pointerIndirectionLevels
204            and param.isConst
205            and "len" in param.attribs)
206
207INTERNAL_OBJECT_NAME = "internal_object"
208
209class VulkanFuncTable(VulkanWrapperGenerator):
210    def __init__(self, module, typeInfo):
211        VulkanWrapperGenerator.__init__(self, module, typeInfo)
212        self.typeInfo = typeInfo
213        self.cgen = CodeGen()
214        self.entries = []
215        self.entryFeatures = []
216        self.cmdToFeatureType = {}
217        self.feature = None
218        self.featureType = None
219
220    def onBegin(self,):
221        cgen = self.cgen
222        self.module.appendImpl(cgen.swapCode())
223        pass
224
225    def onBeginFeature(self, featureName, featureType):
226        self.feature = featureName
227        self.featureType = featureType
228
229    def onEndFeature(self):
230        self.feature = None
231        self.featureType = None
232
233    def onFeatureNewCmd(self, name):
234        self.cmdToFeatureType[name] = self.featureType
235
236    def onGenCmd(self, cmdinfo, name, alias):
237        typeInfo = self.typeInfo
238        cgen = self.cgen
239        api = typeInfo.apis[name]
240        self.entries.append(api)
241        self.entryFeatures.append(self.feature)
242        self.loopVars = ["i", "j", "k", "l", "m", "n"]
243        self.loopVarIndex = 0
244
245        def getNextLoopVar():
246            if self.loopVarIndex >= len(self.loopVars):
247                raise
248            loopVar = self.loopVars[self.loopVarIndex]
249            self.loopVarIndex += 1
250            return loopVar
251
252        def isCompoundType(typeName):
253            return typeInfo.isCompoundType(typeName)
254
255        def handleTranslationRequired(typeName):
256            return typeName in HANDLE_TYPES and typeName in HANDLES_TRANSLATE
257
258        def translationRequired(typeName):
259            if isCompoundType(typeName):
260                struct = typeInfo.structs[typeName]
261                for member in struct.members:
262                    if translationRequired(member.typeName):
263                        return True
264                return False
265            else:
266                return handleTranslationRequired(typeName)
267
268        def genDestroyGfxstreamObjects():
269            destroyParam = getDestroyParam(api)
270            if not destroyParam:
271                return
272            if not translationRequired(destroyParam.typeName):
273                return
274            objectName = paramNameToObjectName(destroyParam.paramName)
275            allocatorParam = "NULL"
276            for p in api.parameters:
277                if isAllocatorParam(p):
278                    allocatorParam = p.paramName
279            deviceParam = api.parameters[0]
280            if "VkDevice" != deviceParam.typeName:
281                print("ERROR: Unhandled non-VkDevice parameters[0]: %s (for API: %s)" %(deviceParam.typeName, api.name))
282                raise
283            # call vk_object_free() directly
284            mesaObjectDestroy = "(void *)%s" % objectName
285            cgen.funcCall(
286                None,
287                "vk_object_free",
288                ["&%s->vk" % paramNameToObjectName(deviceParam.paramName), allocatorParam, mesaObjectDestroy]
289            )
290
291        def genMesaObjectAlloc(allocCallLhs):
292            deviceParam = api.parameters[0]
293            if "VkDevice" != deviceParam.typeName:
294                print("ERROR: Unhandled non-VkDevice parameters[0]: %s (for API: %s)" %(deviceParam.typeName, api.name))
295                raise
296            allocatorParam = "NULL"
297            for p in api.parameters:
298                if isAllocatorParam(p):
299                    allocatorParam = p.paramName
300            createParam = getCreateParam(api)
301            objectType = typeNameToObjectType(createParam.typeName)
302            # Call vk_object_zalloc directly
303            cgen.funcCall(
304                allocCallLhs,
305                "(%s *)vk_object_zalloc" % objectType,
306                ["&%s->vk" % paramNameToObjectName(deviceParam.paramName), allocatorParam, ("sizeof(%s)" % objectType), typeNameToVkObjectType(createParam.typeName)]
307            )
308
309        # Alloc/create gfxstream_vk_* object
310        def genCreateGfxstreamObjects():
311            createParam = getCreateParam(api)
312            if not createParam:
313                return False
314            if not handleTranslationRequired(createParam.typeName):
315                return False
316            objectType = "struct %s" % typeNameToObjectType(createParam.typeName)
317            callLhs = "%s *%s" % (objectType, paramNameToObjectName(createParam.paramName))
318            genMesaObjectAlloc(callLhs)
319
320            retVar = api.getRetVarExpr()
321            if retVar:
322                retTypeName = api.getRetTypeExpr()
323                # ex: vkCreateBuffer_VkResult_return = gfxstream_buffer ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;
324                cgen.stmt("%s = %s ? %s : %s" %
325                          (retVar, paramNameToObjectName(createParam.paramName), SUCCESS_VAL[retTypeName][0], "VK_ERROR_OUT_OF_HOST_MEMORY"))
326            return True
327
328        def genVkFromHandle(param, fromName):
329            objectName = paramNameToObjectName(param.paramName)
330            cgen.stmt("VK_FROM_HANDLE(%s, %s, %s)" %
331                      (typeNameToObjectType(param.typeName), objectName, fromName))
332            return objectName
333
334        def genGetGfxstreamHandles():
335            createParam = getCreateParam(api)
336            for param in api.parameters:
337                if not handleTranslationRequired(param.typeName):
338                    continue
339                elif isArrayParam(param):
340                    continue
341                elif param != createParam:
342                    if param.pointerIndirectionLevels > 0:
343                        print("ERROR: Unhandled pointerIndirectionLevels > 1 for API %s (param %s)" % (api.name, param.paramName))
344                        raise
345                    genVkFromHandle(param, param.paramName)
346
347        def internalNestedParamName(param):
348            parentName = ""
349            if param.parent:
350                parentName = "_%s" % param.parent.typeName
351            return "internal%s_%s" % (parentName, param.paramName)
352
353        def genInternalArrayDeclarations(param, countParamName, nestLevel=0):
354            internalArray = None
355            if 0 == nestLevel:
356                internalArray = "internal_%s" % param.paramName
357                cgen.stmt("std::vector<%s> %s(%s)" % (param.typeName, internalArray, countParamName))
358            elif 1 == nestLevel or 2 == nestLevel:
359                internalArray = internalNestedParamName(param)
360                if isArrayParam(param):
361                    cgen.stmt("std::vector<std::vector<%s>> %s" % (param.typeName, internalArray))
362                else:
363                    cgen.stmt("std::vector<%s> %s" % (param.typeName, internalArray))
364            else:
365                print("ERROR: nestLevel > 2 not verified.")
366                raise
367            if isCompoundType(param.typeName):
368                for member in typeInfo.structs[param.typeName].members:
369                    if translationRequired(member.typeName):
370                        if handleTranslationRequired(member.typeName) and not isArrayParam(member):
371                            # No declarations for non-array handleType
372                            continue
373                        genInternalArrayDeclarations(member, countParamName, nestLevel + 1)
374            return internalArray
375
376        def genInternalCompoundType(param, outName, inName, currLoopVar):
377            nextLoopVar = None
378            cgen.stmt("%s = %s" % (outName, inName))
379            for member in typeInfo.structs[param.typeName].members:
380                if not translationRequired(member.typeName):
381                    continue
382                cgen.line("/* %s::%s */" % (param.typeName, member.paramName))
383                nestedOutName = ("%s[%s]" % (internalNestedParamName(member), currLoopVar))
384                if isArrayParam(member):
385                    countParamName = "%s.%s" % (outName, member.attribs["len"])
386                    inArrayName = "%s.%s" % (outName, member.paramName)
387                    cgen.stmt("%s.push_back(std::vector<%s>())" % (internalNestedParamName(member), member.typeName))
388                    if member.typeName in TYPES_TRANSFORM_LIST_METHOD:
389                        # Use the corresponding transformList call
390                        cgen.funcCall(nestedOutName, transformListFuncName(member.typeName), [inArrayName, countParamName])
391                        cgen.stmt("%s = %s.data()" % (inArrayName, nestedOutName))
392                        cgen.stmt("%s = %s.size()" % (countParamName, nestedOutName))
393                    else:
394                        # Standard translation
395                        cgen.stmt("%s.reserve(%s)" % (nestedOutName, countParamName))
396                        cgen.stmt("memset(&%s[0], 0, sizeof(%s) * %s)" % (nestedOutName, member.typeName, countParamName))
397                        if not nextLoopVar:
398                            nextLoopVar = getNextLoopVar()
399                        internalArray = genInternalArray(member, countParamName, nestedOutName, inArrayName, nextLoopVar)
400                        cgen.stmt("%s = %s" %(inArrayName, internalArray))
401                elif isCompoundType(member.typeName):
402                    memberFullName = "%s.%s" % (outName, member.paramName)
403                    if 1 == member.pointerIndirectionLevels:
404                        cgen.beginIf(memberFullName)
405                        inParamName = "%s[0]" % memberFullName
406                        genInternalCompoundType(member, nestedOutName, inParamName, currLoopVar)
407                        cgen.stmt("%s.%s = &%s" % (outName, member.paramName,  nestedOutName))
408                    else:
409                        cgen.beginBlock()
410                        genInternalCompoundType(member, nestedOutName, memberFullName, currLoopVar)
411                        cgen.stmt("%s.%s = %s" % (outName, member.paramName,  nestedOutName))
412                    cgen.endBlock()
413                else:
414                    # Replace member with internal object
415                    replaceName = "%s.%s" % (outName, member.paramName)
416                    if member.isOptional:
417                        cgen.beginIf(replaceName)
418                    gfxstreamObject = genVkFromHandle(member, replaceName)
419                    cgen.stmt("%s = %s->%s" % (replaceName, gfxstreamObject, INTERNAL_OBJECT_NAME))
420                    if member.isOptional:
421                        cgen.endIf()
422
423        def genInternalArray(param, countParamName, outArrayName, inArrayName, loopVar):
424            if param.typeName in TYPES_TRANSFORM_LIST_METHOD:
425                # Use the corresponding transformList call
426                cgen.funcCall(outArrayName, transformListFuncName(param.typeName), [inArrayName, countParamName])
427                cgen.stmt("%s = %s.data()" % (inArrayName, outArrayName))
428                cgen.stmt("%s = %s.size()" % (countParamName, outArrayName))
429            else:
430                cgen.beginFor("uint32_t %s = 0" % loopVar, "%s < %s" % (loopVar, countParamName), "++%s" % loopVar)
431                if param.isOptional:
432                    cgen.beginIf(inArrayName)
433                if isCompoundType(param.typeName):
434                    genInternalCompoundType(param, ("%s[%s]" % (outArrayName, loopVar)), "%s[%s]" % (inArrayName, loopVar), loopVar)
435                else:
436                    gfxstreamObject = genVkFromHandle(param, "%s[%s]" % (inArrayName, loopVar))
437                    cgen.stmt("%s[%s] = %s->%s" % (outArrayName, loopVar, gfxstreamObject, INTERNAL_OBJECT_NAME))
438                if param.isOptional:
439                    cgen.endIf()
440                cgen.endFor()
441            return "%s.data()" % outArrayName
442
443        # Translate params into params needed for gfxstream-internal
444        #  encoder/resource-tracker calls
445        def getEncoderOrResourceTrackerParams():
446            createParam = getCreateParam(api)
447            outParams = copy.deepcopy(api.parameters)
448            nextLoopVar = getNextLoopVar()
449            for param in outParams:
450                if not translationRequired(param.typeName):
451                    continue
452                elif isArrayParam(param) or isCompoundType(param.typeName):
453                    if param.possiblyOutput():
454                        print("ERROR: Unhandled CompoundType / Array output for API %s (param %s)" % (api.name, param.paramName))
455                        raise
456                    if 1 != param.pointerIndirectionLevels or not param.isConst:
457                        print("ERROR: Compound type / array input is not 'const <type>*' (API: %s, paramName: %s)" % (api.name, param.paramName))
458                        raise
459                    countParamName = "1"
460                    if "len" in param.attribs:
461                        countParamName = param.attribs["len"]
462                    internalArrayName = genInternalArrayDeclarations(param, countParamName)
463                    param.paramName = genInternalArray(param, countParamName, internalArrayName, param.paramName, nextLoopVar)
464                elif 0 == param.pointerIndirectionLevels:
465                    if param.isOptional:
466                        param.paramName = ("%s ? %s->%s : VK_NULL_HANDLE" % (paramNameToObjectName(param.paramName), paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
467                    else:
468                        param.paramName = ("%s->%s" % (paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
469                elif createParam and param.paramName == createParam.paramName:
470                    param.paramName = ("&%s->%s" % (paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
471                else:
472                    print("ERROR: Unknown handling for param: %s (API: %s)" % (param, api.name))
473                    raise
474            return outParams
475
476        def genEncoderOrResourceTrackerCall(declareResources=True):
477            if is_cmdbuf_dispatch(api):
478                cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getCommandBufferEncoder(%s->%s)" % (paramNameToObjectName(api.parameters[0].paramName), INTERNAL_OBJECT_NAME))
479            elif is_queue_dispatch(api):
480                cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getQueueEncoder(%s->%s)" % (paramNameToObjectName(api.parameters[0].paramName), INTERNAL_OBJECT_NAME))
481            else:
482                cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getThreadLocalEncoder()")
483            callLhs = None
484            retTypeName = api.getRetTypeExpr()
485            if retTypeName != "void":
486                callLhs = api.getRetVarExpr()
487
488            # Get parameter list modded for gfxstream-internal call
489            parameters = getEncoderOrResourceTrackerParams()
490            if name in RESOURCE_TRACKER_ENTRIES:
491                if declareResources:
492                    cgen.stmt("auto resources = gfxstream::vk::ResourceTracker::get()")
493                cgen.funcCall(
494                    callLhs, "resources->" + "on_" + api.name,
495                    ["vkEnc"] + SUCCESS_VAL.get(retTypeName, []) + \
496                    [p.paramName for p in parameters])
497            else:
498                cgen.funcCall(
499                    callLhs, "vkEnc->" + api.name, [p.paramName for p in parameters] + ["true /* do lock */"])
500
501        def genReturnExpression():
502            retTypeName = api.getRetTypeExpr()
503            # Set the createParam output, if applicable
504            createParam = getCreateParam(api)
505            if createParam and handleTranslationRequired(createParam.typeName):
506                if 1 != createParam.pointerIndirectionLevels:
507                    print("ERROR: Unhandled pointerIndirectionLevels != 1 in return for API %s (createParam %s)" % api.name, createParam.paramName)
508                    raise
509                # ex: *pBuffer = gfxstream_vk_buffer_to_handle(gfxstream_buffer)
510                cgen.funcCall(
511                    "*%s" % createParam.paramName,
512                    "%s_to_handle" % typeNameToObjectType(createParam.typeName),
513                    [paramNameToObjectName(createParam.paramName)]
514                )
515
516            if retTypeName != "void":
517                cgen.stmt("return %s" % api.getRetVarExpr())
518
519        def genGfxstreamEntry(declareResources=True):
520            cgen.stmt("AEMU_SCOPED_TRACE(\"%s\")" % api.name)
521            # declare returnVar
522            retTypeName = api.getRetTypeExpr()
523            retVar = api.getRetVarExpr()
524            if retVar:
525                cgen.stmt("%s %s = (%s)0" % (retTypeName, retVar, retTypeName))
526            # Check non-null destroy param for free/destroy calls
527            destroyParam = getDestroyParam(api)
528            if destroyParam:
529                cgen.beginIf("VK_NULL_HANDLE == %s" % destroyParam.paramName)
530                if api.getRetTypeExpr() != "void":
531                    cgen.stmt("return %s" % api.getRetVarExpr())
532                else:
533                    cgen.stmt("return")
534                cgen.endIf()
535            # Translate handles
536            genGetGfxstreamHandles()
537            # Translation/creation of objects
538            createdObject = genCreateGfxstreamObjects()
539            # Make encoder/resource-tracker call
540            if retVar and createdObject:
541                cgen.beginIf("%s == %s" % (SUCCESS_VAL[retTypeName][0], retVar))
542            else:
543                cgen.beginBlock()
544            genEncoderOrResourceTrackerCall()
545            cgen.endBlock()
546            # Destroy gfxstream objects
547            genDestroyGfxstreamObjects()
548            # Set output / return variables
549            genReturnExpression()
550
551        api_entry = api.withModifiedName("gfxstream_vk_" + api.name[2:])
552        if api.name not in HANDWRITTEN_ENTRY_POINTS:
553            cgen.line(self.cgen.makeFuncProto(api_entry))
554            cgen.beginBlock()
555            genGfxstreamEntry()
556            cgen.endBlock()
557            self.module.appendImpl(cgen.swapCode())
558
559
560    def onEnd(self,):
561        pass
562
563    def isDeviceDispatch(self, api):
564        # TODO(230793667): improve the heuristic and just use "cmdToFeatureType"
565        return (len(api.parameters) > 0 and
566            "VkDevice" == api.parameters[0].typeName) or (
567            "VkCommandBuffer" == api.parameters[0].typeName and
568            self.cmdToFeatureType.get(api.name, "") == "device")
569