1import os 2from typing import List, Set, Dict, Optional 3 4from . import VulkanType, VulkanCompoundType 5from .wrapperdefs import VulkanWrapperGenerator 6 7 8class ApiLogDecoder(VulkanWrapperGenerator): 9 """ 10 This class generates decoding logic for the graphics API logs captured by 11 [GfxApiLogger](http://source/play-internal/battlestar/aosp/device/generic/vulkan-cereal/base/GfxApiLogger.h) 12 13 This allows developers to see a pretty-printed version of the API log data when using 14 print_gfx_logs.py 15 """ 16 17 # List of Vulkan APIs that we will generate decoding logic for 18 generated_apis = [ 19 "vkAcquireImageANDROID", 20 "vkAllocateMemory", 21 "vkBeginCommandBufferAsyncGOOGLE", 22 "vkBindBufferMemory", 23 "vkBindImageMemory", 24 "vkCmdBeginRenderPass", 25 "vkCmdBindDescriptorSets", 26 "vkCmdBindIndexBuffer", 27 "vkCmdBindPipeline", 28 "vkCmdBindVertexBuffers", 29 "vkCmdClearAttachments", 30 "vkCmdClearColorImage", 31 "vkCmdCopyBufferToImage", 32 "vkCmdCopyImageToBuffer", 33 "vkCmdDraw", 34 "vkCmdDrawIndexed", 35 "vkCmdEndRenderPass", 36 "vkCmdPipelineBarrier", 37 "vkCmdSetScissor", 38 "vkCmdSetViewport", 39 "vkCollectDescriptorPoolIdsGOOGLE", 40 "vkCreateBufferWithRequirementsGOOGLE", 41 "vkCreateDescriptorPool", 42 "vkCreateDescriptorSetLayout", 43 "vkCreateFence", 44 "vkCreateFramebuffer", 45 "vkCreateGraphicsPipelines", 46 "vkCreateImageView", 47 "vkCreateImageWithRequirementsGOOGLE", 48 "vkCreatePipelineCache", 49 "vkCreateRenderPass", 50 "vkCreateSampler", 51 "vkCreateSemaphore", 52 "vkCreateShaderModule", 53 "vkDestroyBuffer", 54 "vkDestroyCommandPool", 55 "vkDestroyDescriptorPool", 56 "vkDestroyDescriptorSetLayout", 57 "vkDestroyDevice", 58 "vkDestroyFence", 59 "vkDestroyFramebuffer", 60 "vkDestroyImage", 61 "vkDestroyImageView", 62 "vkDestroyInstance", 63 "vkDestroyPipeline", 64 "vkDestroyPipelineCache", 65 "vkDestroyPipelineLayout", 66 "vkDestroyRenderPass", 67 "vkDestroySemaphore", 68 "vkDestroyShaderModule", 69 "vkEndCommandBufferAsyncGOOGLE", 70 "vkFreeCommandBuffers", 71 "vkFreeMemory", 72 "vkFreeMemorySyncGOOGLE", 73 "vkGetFenceStatus", 74 "vkGetMemoryHostAddressInfoGOOGLE", 75 "vkGetBlobGOOGLE", 76 "vkGetPhysicalDeviceFormatProperties", 77 "vkGetPhysicalDeviceProperties2KHR", 78 "vkGetPipelineCacheData", 79 "vkGetSwapchainGrallocUsageANDROID", 80 "vkQueueCommitDescriptorSetUpdatesGOOGLE", 81 "vkQueueFlushCommandsGOOGLE", 82 "vkQueueSignalReleaseImageANDROIDAsyncGOOGLE", 83 "vkQueueSubmitAsyncGOOGLE", 84 "vkQueueWaitIdle", 85 "vkResetFences", 86 "vkWaitForFences", 87 ] 88 89 def __init__(self, module, typeInfo): 90 VulkanWrapperGenerator.__init__(self, module, typeInfo) 91 self.typeInfo = typeInfo 92 93 # Set of Vulkan structs that we need to write decoding logic for 94 self.structs: Set[str] = set() 95 96 # Maps enum group names to the list of enums in the group, for all enum groups in the spec 97 # E.g.: "VkResult": ["VK_SUCCESS", "VK_NOT_READY", "VK_TIMEOUT", etc...] 98 self.all_enums: Dict[str, List[str]] = {} 99 100 # Set of Vulkan enums that we need to write decoding logic for 101 self.needed_enums: Set[str] = {"VkStructureType"} 102 103 def onBegin(self): 104 self.module.append(""" 105##################################################################################################### 106# Pretty-printer functions for Vulkan data structures 107# THIS FILE IS AUTO-GENERATED - DO NOT EDIT 108# 109# To re-generate this file, run generate-vulkan-sources.sh 110##################################################################################################### 111 112""".lstrip()) 113 114 def onGenGroup(self, groupinfo, groupName, alias=None): 115 """Called for each enum group in the spec""" 116 for enum in groupinfo.elem.findall("enum"): 117 self.all_enums[groupName] = self.all_enums.get(groupName, []) + [enum.get('name')] 118 119 def onEnd(self): 120 for api_name in sorted(self.generated_apis): 121 self.process_api(api_name) 122 self.process_structs() 123 self.process_enums() 124 125 def process_api(self, api_name): 126 """Main entry point to generate decoding logic for each Vulkan API""" 127 api = self.typeInfo.apis[api_name] 128 self.module.append('def OP_{}(printer, indent: int):\n'.format(api_name)) 129 130 # Decode the sequence number. All commands have sequence numbers, except those handled 131 # by VkSubdecoder.cpp. The logic here is a bit of a hack since it's based on the command 132 # name. Ideally, we would detect whether a particular command is part of a subdecode block 133 # in the decoding script. 134 if not api_name.startswith("vkCmd") and api_name != "vkBeginCommandBufferAsyncGOOGLE": 135 self.module.append(' printer.write_int("seqno: ", 4, indent)\n') 136 137 for param in api.parameters: 138 # Add any structs that this API uses to the list of structs to write decoding logic for 139 if self.typeInfo.isCompoundType(param.typeName): 140 self.structs.add(param.typeName) 141 142 # Don't try to print the pData field of vkQueueFlushCommandsGOOGLE, those are the 143 # commands processed as part of the subdecode pass 144 if api.name == "vkQueueFlushCommandsGOOGLE" and param.paramName == "pData": 145 continue 146 147 # Write out decoding logic for that parameter 148 self.process_type(param) 149 150 # Finally, add a return statement. This is needed in case the API has no parameters. 151 self.module.append(' return\n\n') 152 153 def process_structs(self): 154 """Writes decoding logic for all the structs that we use""" 155 156 # self.structs now contains all the structs used directly by the Vulkan APIs we use. 157 # Recursively expand this set to add all the structs used by these structs. 158 copy = self.structs.copy() 159 self.structs.clear() 160 for struct_name in copy: 161 self.expand_needed_structs(struct_name) 162 163 # Now we have the full list of structs that we need to write decoding logic for. 164 # Write a decoder for each of them 165 for struct_name in sorted(self.structs): 166 struct = self.typeInfo.structs[struct_name] 167 self.module.append('def struct_{}(printer, indent: int):\n'.format(struct_name)) 168 for member in self.get_members(struct): 169 self.process_type(member) 170 self.module.append('\n') 171 172 def expand_needed_structs(self, struct_name: str): 173 """ 174 Recursively adds all the structs used by a given struct to the list of structs to process 175 """ 176 if struct_name in self.structs: 177 return 178 self.structs.add(struct_name) 179 struct = self.typeInfo.structs[struct_name] 180 for member in self.get_members(struct): 181 if self.typeInfo.isCompoundType(member.typeName): 182 self.expand_needed_structs(member.typeName) 183 184 def get_members(self, struct: VulkanCompoundType): 185 """ 186 Returns the members of a struct/union that we need to process. 187 For structs, returns the list of all members 188 For unions, returns a list with just the first member. 189 """ 190 return struct.members[0:1] if struct.isUnion else struct.members 191 192 def process_type(self, type: VulkanType): 193 """ 194 Writes decoding logic for a single Vulkan type. This could be the parameter in a Vulkan API, 195 or a struct member. 196 """ 197 if type.typeName == "VkStructureType": 198 self.module.append( 199 ' printer.write_stype_and_pnext("{}", indent)\n'.format( 200 type.parent.structEnumExpr)) 201 return 202 203 if type.isNextPointer(): 204 return 205 206 if type.paramName == "commandBuffer": 207 if type.parent.name != "vkQueueFlushCommandsGOOGLE": 208 return 209 210 # Enums 211 if type.isEnum(self.typeInfo): 212 self.needed_enums.add(type.typeName) 213 self.module.append( 214 ' printer.write_enum("{}", {}, indent)\n'.format( 215 type.paramName, type.typeName)) 216 return 217 218 # Bitmasks 219 if type.isBitmask(self.typeInfo): 220 enum_type = self.typeInfo.bitmasks.get(type.typeName) 221 if enum_type: 222 self.needed_enums.add(enum_type) 223 self.module.append( 224 ' printer.write_flags("{}", {}, indent)\n'.format( 225 type.paramName, enum_type)) 226 return 227 # else, fall through and let the primitive type logic handle it 228 229 # Structs or unions 230 if self.typeInfo.isCompoundType(type.typeName): 231 self.module.append( 232 ' printer.write_struct("{name}", struct_{type}, {optional}, {count}, indent)\n' 233 .format(name=type.paramName, 234 type=type.typeName, 235 optional=type.isOptionalPointer(), 236 count=self.get_length_expression(type))) 237 return 238 239 # Null-terminated strings 240 if type.isString(): 241 self.module.append(' printer.write_string("{}", None, indent)\n'.format( 242 type.paramName)) 243 return 244 245 # Arrays of primitive types 246 if type.staticArrExpr and type.primitiveEncodingSize and type.primitiveEncodingSize <= 8: 247 # Array sizes are specified either as a number, or as an enum value 248 array_size = int(type.staticArrExpr) if type.staticArrExpr.isdigit() \ 249 else self.typeInfo.enumValues.get(type.staticArrExpr) 250 assert array_size is not None, type.staticArrExpr 251 252 if type.typeName == "char": 253 self.module.append( 254 ' printer.write_string("{}", {}, indent)\n'.format( 255 type.paramName, array_size)) 256 elif type.typeName == "float": 257 self.module.append( 258 ' printer.write_float("{}", indent, count={})\n' 259 .format(type.paramName, array_size)) 260 else: 261 self.module.append( 262 ' printer.write_int("{name}", {int_size}, indent, signed={signed}, count={array_size})\n' 263 .format(name=type.paramName, 264 array_size=array_size, 265 int_size=type.primitiveEncodingSize, 266 signed=type.isSigned())) 267 return 268 269 # Pointers 270 if type.pointerIndirectionLevels > 0: 271 # Assume that all uint32* are always serialized directly rather than passed by pointers. 272 # This is probably not always true (e.g. out params) - fix this as needed. 273 size = 4 if type.primitiveEncodingSize == 4 else 8 274 self.module.append( 275 ' {name} = printer.write_int("{name}", {size}, indent, optional={opt}, count={count}, big_endian={big_endian})\n' 276 .format(name=type.paramName, 277 size=size, 278 opt=type.isOptionalPointer(), 279 count=self.get_length_expression(type), 280 big_endian=self.using_big_endian(type))) 281 return 282 283 # Primitive types (ints, floats) 284 if type.isSimpleValueType(self.typeInfo) and type.primitiveEncodingSize: 285 if type.typeName == "float": 286 self.module.append( 287 ' printer.write_float("{name}", indent)\n'.format(name=type.paramName)) 288 else: 289 self.module.append( 290 ' {name} = printer.write_int("{name}", {size}, indent, signed={signed}, big_endian={big_endian})\n'.format( 291 name=type.paramName, 292 size=type.primitiveEncodingSize, 293 signed=type.isSigned(), 294 big_endian=self.using_big_endian(type)) 295 ) 296 return 297 298 raise NotImplementedError( 299 "No decoding logic for {} {}".format(type.typeName, type.paramName)) 300 301 def using_big_endian(self, type: VulkanType): 302 """For some reason gfxstream serializes some types as big endian""" 303 return type.typeName == "size_t" 304 305 def get_length_expression(self, type: VulkanType) -> Optional[str]: 306 """Returns the length expression for a given type""" 307 if type.lenExpr is None: 308 return None 309 310 if type.lenExpr.isalpha(): 311 return type.lenExpr 312 313 # There are a couple of instances in the spec where we use a math expression to express the 314 # length (e.g. VkPipelineMultisampleStateCreateInfo). CodeGen().generalLengthAccess() has 315 # logic o parse these expressions correctly, but for now,we just use a simple lookup table. 316 known_expressions = { 317 r"latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]": 318 "int(rasterizationSamples / 32)", 319 r"latexmath:[\textrm{codeSize} \over 4]": "int(codeSize / 4)", 320 r"null-terminated": None 321 } 322 if type.lenExpr in known_expressions: 323 return known_expressions[type.lenExpr] 324 325 raise NotImplementedError("Unknown length expression: " + type.lenExpr) 326 327 def process_enums(self): 328 """ 329 For each Vulkan enum that we use, write out a python dictionary mapping the enum values back 330 to the enum name as a string 331 """ 332 for enum_name in sorted(self.needed_enums): 333 self.module.append('{} = {{\n'.format(enum_name)) 334 for identifier in self.all_enums[enum_name]: 335 value = self.typeInfo.enumValues.get(identifier) 336 if value is not None and isinstance(value, int): 337 self.module.append(' {}: "{}",\n'.format(value, identifier)) 338 self.module.append('}\n\n') 339