summaryrefslogtreecommitdiff
path: root/registry/vulkan/scripts/cereal/api_log_decoder.py
diff options
context:
space:
mode:
Diffstat (limited to 'registry/vulkan/scripts/cereal/api_log_decoder.py')
-rw-r--r--registry/vulkan/scripts/cereal/api_log_decoder.py337
1 files changed, 337 insertions, 0 deletions
diff --git a/registry/vulkan/scripts/cereal/api_log_decoder.py b/registry/vulkan/scripts/cereal/api_log_decoder.py
new file mode 100644
index 00000000..dfca0717
--- /dev/null
+++ b/registry/vulkan/scripts/cereal/api_log_decoder.py
@@ -0,0 +1,337 @@
+import os
+from typing import List, Set, Dict, Optional
+
+from . import VulkanType, VulkanCompoundType
+from .wrapperdefs import VulkanWrapperGenerator
+
+
+class ApiLogDecoder(VulkanWrapperGenerator):
+ """
+ This class generates decoding logic for the graphics API logs captured by
+ [GfxApiLogger](http://source/play-internal/battlestar/aosp/device/generic/vulkan-cereal/base/GfxApiLogger.h)
+
+ This allows developers to see a pretty-printed version of the API log data when using
+ print_gfx_logs.py
+ """
+
+ # List of Vulkan APIs that we will generate decoding logic for
+ generated_apis = [
+ "vkAcquireImageANDROID",
+ "vkAllocateMemory",
+ "vkBeginCommandBufferAsyncGOOGLE",
+ "vkBindBufferMemory",
+ "vkBindImageMemory",
+ "vkCmdBeginRenderPass",
+ "vkCmdBindDescriptorSets",
+ "vkCmdBindIndexBuffer",
+ "vkCmdBindPipeline",
+ "vkCmdBindVertexBuffers",
+ "vkCmdClearAttachments",
+ "vkCmdClearColorImage",
+ "vkCmdCopyBufferToImage",
+ "vkCmdCopyImageToBuffer",
+ "vkCmdDraw",
+ "vkCmdDrawIndexed",
+ "vkCmdEndRenderPass",
+ "vkCmdPipelineBarrier",
+ "vkCmdSetScissor",
+ "vkCmdSetViewport",
+ "vkCollectDescriptorPoolIdsGOOGLE",
+ "vkCreateBufferWithRequirementsGOOGLE",
+ "vkCreateDescriptorPool",
+ "vkCreateDescriptorSetLayout",
+ "vkCreateFence",
+ "vkCreateFramebuffer",
+ "vkCreateGraphicsPipelines",
+ "vkCreateImageView",
+ "vkCreateImageWithRequirementsGOOGLE",
+ "vkCreatePipelineCache",
+ "vkCreateRenderPass",
+ "vkCreateSampler",
+ "vkCreateSemaphore",
+ "vkCreateShaderModule",
+ "vkDestroyBuffer",
+ "vkDestroyCommandPool",
+ "vkDestroyDescriptorPool",
+ "vkDestroyDescriptorSetLayout",
+ "vkDestroyDevice",
+ "vkDestroyFence",
+ "vkDestroyFramebuffer",
+ "vkDestroyImage",
+ "vkDestroyImageView",
+ "vkDestroyInstance",
+ "vkDestroyPipeline",
+ "vkDestroyPipelineCache",
+ "vkDestroyPipelineLayout",
+ "vkDestroyRenderPass",
+ "vkDestroySemaphore",
+ "vkDestroyShaderModule",
+ "vkEndCommandBufferAsyncGOOGLE",
+ "vkFreeCommandBuffers",
+ "vkFreeMemory",
+ "vkFreeMemorySyncGOOGLE",
+ "vkGetFenceStatus",
+ "vkGetMemoryHostAddressInfoGOOGLE",
+ "vkGetPhysicalDeviceFormatProperties",
+ "vkGetPhysicalDeviceProperties2KHR",
+ "vkGetPipelineCacheData",
+ "vkGetSwapchainGrallocUsageANDROID",
+ "vkQueueCommitDescriptorSetUpdatesGOOGLE",
+ "vkQueueFlushCommandsGOOGLE",
+ "vkQueueSignalReleaseImageANDROIDAsyncGOOGLE",
+ "vkQueueSubmitAsyncGOOGLE",
+ "vkQueueWaitIdle",
+ "vkResetFences",
+ "vkWaitForFences",
+ ]
+
+ def __init__(self, module, typeInfo):
+ VulkanWrapperGenerator.__init__(self, module, typeInfo)
+ self.typeInfo = typeInfo
+
+ # Set of Vulkan structs that we need to write decoding logic for
+ self.structs: Set[str] = set()
+
+ # Maps enum group names to the list of enums in the group, for all enum groups in the spec
+ # E.g.: "VkResult": ["VK_SUCCESS", "VK_NOT_READY", "VK_TIMEOUT", etc...]
+ self.all_enums: Dict[str, List[str]] = {}
+
+ # Set of Vulkan enums that we need to write decoding logic for
+ self.needed_enums: Set[str] = {"VkStructureType"}
+
+ def onBegin(self):
+ self.module.append("""
+#####################################################################################################
+# Pretty-printer functions for Vulkan data structures
+# THIS FILE IS AUTO-GENERATED - DO NOT EDIT
+#
+# To re-generate this file, run generate-vulkan-sources.sh
+#####################################################################################################
+
+""".lstrip())
+
+ def onGenGroup(self, groupinfo, groupName, alias=None):
+ """Called for each enum group in the spec"""
+ for enum in groupinfo.elem.findall("enum"):
+ self.all_enums[groupName] = self.all_enums.get(groupName, []) + [enum.get('name')]
+
+ def onEnd(self):
+ for api_name in sorted(self.generated_apis):
+ self.process_api(api_name)
+ self.process_structs()
+ self.process_enums()
+
+ def process_api(self, api_name):
+ """Main entry point to generate decoding logic for each Vulkan API"""
+ api = self.typeInfo.apis[api_name]
+ self.module.append('def OP_{}(printer, indent: int):\n'.format(api_name))
+
+ # Decode the sequence number. All commands have sequence numbers, except those handled
+ # by VkSubdecoder.cpp. The logic here is a bit of a hack since it's based on the command
+ # name. Ideally, we would detect whether a particular command is part of a subdecode block
+ # in the decoding script.
+ if not api_name.startswith("vkCmd") and api_name != "vkBeginCommandBufferAsyncGOOGLE":
+ self.module.append(' printer.write_int("seqno: ", 4, indent)\n')
+
+ for param in api.parameters:
+ # Add any structs that this API uses to the list of structs to write decoding logic for
+ if self.typeInfo.isCompoundType(param.typeName):
+ self.structs.add(param.typeName)
+
+ # Don't try to print the pData field of vkQueueFlushCommandsGOOGLE, those are the
+ # commands processed as part of the subdecode pass
+ if api.name == "vkQueueFlushCommandsGOOGLE" and param.paramName == "pData":
+ continue
+
+ # Write out decoding logic for that parameter
+ self.process_type(param)
+
+ # Finally, add a return statement. This is needed in case the API has no parameters.
+ self.module.append(' return\n\n')
+
+ def process_structs(self):
+ """Writes decoding logic for all the structs that we use"""
+
+ # self.structs now contains all the structs used directly by the Vulkan APIs we use.
+ # Recursively expand this set to add all the structs used by these structs.
+ copy = self.structs.copy()
+ self.structs.clear()
+ for struct_name in copy:
+ self.expand_needed_structs(struct_name)
+
+ # Now we have the full list of structs that we need to write decoding logic for.
+ # Write a decoder for each of them
+ for struct_name in sorted(self.structs):
+ struct = self.typeInfo.structs[struct_name]
+ self.module.append('def struct_{}(printer, indent: int):\n'.format(struct_name))
+ for member in self.get_members(struct):
+ self.process_type(member)
+ self.module.append('\n')
+
+ def expand_needed_structs(self, struct_name: str):
+ """
+ Recursively adds all the structs used by a given struct to the list of structs to process
+ """
+ if struct_name in self.structs:
+ return
+ self.structs.add(struct_name)
+ struct = self.typeInfo.structs[struct_name]
+ for member in self.get_members(struct):
+ if self.typeInfo.isCompoundType(member.typeName):
+ self.expand_needed_structs(member.typeName)
+
+ def get_members(self, struct: VulkanCompoundType):
+ """
+ Returns the members of a struct/union that we need to process.
+ For structs, returns the list of all members
+ For unions, returns a list with just the first member.
+ """
+ return struct.members[0:1] if struct.isUnion else struct.members
+
+ def process_type(self, type: VulkanType):
+ """
+ Writes decoding logic for a single Vulkan type. This could be the parameter in a Vulkan API,
+ or a struct member.
+ """
+ if type.typeName == "VkStructureType":
+ self.module.append(
+ ' printer.write_stype_and_pnext("{}", indent)\n'.format(
+ type.parent.structEnumExpr))
+ return
+
+ if type.isNextPointer():
+ return
+
+ if type.paramName == "commandBuffer":
+ if type.parent.name != "vkQueueFlushCommandsGOOGLE":
+ return
+
+ # Enums
+ if type.isEnum(self.typeInfo):
+ self.needed_enums.add(type.typeName)
+ self.module.append(
+ ' printer.write_enum("{}", {}, indent)\n'.format(
+ type.paramName, type.typeName))
+ return
+
+ # Bitmasks
+ if type.isBitmask(self.typeInfo):
+ enum_type = self.typeInfo.bitmasks.get(type.typeName)
+ if enum_type:
+ self.needed_enums.add(enum_type)
+ self.module.append(
+ ' printer.write_flags("{}", {}, indent)\n'.format(
+ type.paramName, enum_type))
+ return
+ # else, fall through and let the primitive type logic handle it
+
+ # Structs or unions
+ if self.typeInfo.isCompoundType(type.typeName):
+ self.module.append(
+ ' printer.write_struct("{name}", struct_{type}, {optional}, {count}, indent)\n'
+ .format(name=type.paramName,
+ type=type.typeName,
+ optional=type.isOptionalPointer(),
+ count=self.get_length_expression(type)))
+ return
+
+ # Null-terminated strings
+ if type.isString():
+ self.module.append(' printer.write_string("{}", None, indent)\n'.format(
+ type.paramName))
+ return
+
+ # Arrays of primitive types
+ if type.staticArrExpr and type.primitiveEncodingSize and type.primitiveEncodingSize <= 8:
+ # Array sizes are specified either as a number, or as an enum value
+ array_size = int(type.staticArrExpr) if type.staticArrExpr.isdigit() \
+ else self.typeInfo.enumValues.get(type.staticArrExpr)
+ assert array_size is not None, type.staticArrExpr
+
+ if type.typeName == "char":
+ self.module.append(
+ ' printer.write_string("{}", {}, indent)\n'.format(
+ type.paramName, array_size))
+ elif type.typeName == "float":
+ self.module.append(
+ ' printer.write_float("{}", indent, count={})\n'
+ .format(type.paramName, array_size))
+ else:
+ self.module.append(
+ ' printer.write_int("{name}", {int_size}, indent, signed={signed}, count={array_size})\n'
+ .format(name=type.paramName,
+ array_size=array_size,
+ int_size=type.primitiveEncodingSize,
+ signed=type.isSigned()))
+ return
+
+ # Pointers
+ if type.pointerIndirectionLevels > 0:
+ # Assume that all uint32* are always serialized directly rather than passed by pointers.
+ # This is probably not always true (e.g. out params) - fix this as needed.
+ size = 4 if type.primitiveEncodingSize == 4 else 8
+ self.module.append(
+ ' {name} = printer.write_int("{name}", {size}, indent, optional={opt}, count={count}, big_endian={big_endian})\n'
+ .format(name=type.paramName,
+ size=size,
+ opt=type.isOptionalPointer(),
+ count=self.get_length_expression(type),
+ big_endian=self.using_big_endian(type)))
+ return
+
+ # Primitive types (ints, floats)
+ if type.isSimpleValueType(self.typeInfo) and type.primitiveEncodingSize:
+ if type.typeName == "float":
+ self.module.append(
+ ' printer.write_float("{name}", indent)\n'.format(name=type.paramName))
+ else:
+ self.module.append(
+ ' {name} = printer.write_int("{name}", {size}, indent, signed={signed}, big_endian={big_endian})\n'.format(
+ name=type.paramName,
+ size=type.primitiveEncodingSize,
+ signed=type.isSigned(),
+ big_endian=self.using_big_endian(type))
+ )
+ return
+
+ raise NotImplementedError(
+ "No decoding logic for {} {}".format(type.typeName, type.paramName))
+
+ def using_big_endian(self, type: VulkanType):
+ """For some reason gfxstream serializes some types as big endian"""
+ return type.typeName == "size_t"
+
+ def get_length_expression(self, type: VulkanType) -> Optional[str]:
+ """Returns the length expression for a given type"""
+ if type.lenExpr is None:
+ return None
+
+ if type.lenExpr.isalpha():
+ return type.lenExpr
+
+ # There are a couple of instances in the spec where we use a math expression to express the
+ # length (e.g. VkPipelineMultisampleStateCreateInfo). CodeGen().generalLengthAccess() has
+ # logic o parse these expressions correctly, but for now,we just use a simple lookup table.
+ known_expressions = {
+ r"latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]":
+ "int(rasterizationSamples / 32)",
+ r"latexmath:[\textrm{codeSize} \over 4]": "int(codeSize / 4)",
+ r"null-terminated": None
+ }
+ if type.lenExpr in known_expressions:
+ return known_expressions[type.lenExpr]
+
+ raise NotImplementedError("Unknown length expression: " + type.lenExpr)
+
+ def process_enums(self):
+ """
+ For each Vulkan enum that we use, write out a python dictionary mapping the enum values back
+ to the enum name as a string
+ """
+ for enum_name in sorted(self.needed_enums):
+ self.module.append('{} = {{\n'.format(enum_name))
+ for identifier in self.all_enums[enum_name]:
+ value = self.typeInfo.enumValues.get(identifier)
+ if value is not None and isinstance(value, int):
+ self.module.append(' {}: "{}",\n'.format(value, identifier))
+ self.module.append('}\n\n')