diff --git a/src/gfxstream/codegen/scripts/cereal/__init__.py b/src/gfxstream/codegen/scripts/cereal/__init__.py new file mode 100644 index 00000000000..1966572ffc4 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/__init__.py @@ -0,0 +1,20 @@ +from .common import * +from .decoder import * +from .encoder import * +from .extensionstructs import * +from .frontend import * +from .functable import * +from .marshaling import * +from .reservedmarshaling import * +from .counting import * +from .testing import * +from .transform import * +from .deepcopy import * +from .handlemap import * +from .dispatch import * +from .unbox import * +from .decodersnapshot import * +from .subdecode import * +from .api_log_decoder import * +from .vkextensionstructuretype import VulkanGfxstreamStructureType, \ + VulkanAndroidNativeBufferStructureType diff --git a/src/gfxstream/codegen/scripts/cereal/api_log_decoder.py b/src/gfxstream/codegen/scripts/cereal/api_log_decoder.py new file mode 100644 index 00000000000..97930f5f080 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/api_log_decoder.py @@ -0,0 +1,338 @@ +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", + "vkGetBlobGOOGLE", + "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') diff --git a/src/gfxstream/codegen/scripts/cereal/common/__init__.py b/src/gfxstream/codegen/scripts/cereal/common/__init__.py new file mode 100644 index 00000000000..dd6cdc998a7 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/common/__init__.py @@ -0,0 +1,2 @@ +from .vulkantypes import * +from .codegen import * diff --git a/src/gfxstream/codegen/scripts/cereal/common/codegen.py b/src/gfxstream/codegen/scripts/cereal/common/codegen.py new file mode 100644 index 00000000000..1f217e3bebb --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/common/codegen.py @@ -0,0 +1,1074 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .vulkantypes import VulkanType, VulkanTypeInfo, VulkanCompoundType, VulkanAPI +from collections import OrderedDict +from copy import copy +from pathlib import Path, PurePosixPath + +import os +import sys +import shutil +import subprocess + +# Class capturing a single file + + +class SingleFileModule(object): + def __init__(self, suffix, directory, basename, customAbsDir=None, suppress=False): + self.directory = directory + self.basename = basename + self.customAbsDir = customAbsDir + self.suffix = suffix + self.file = None + + self.preamble = "" + self.postamble = "" + + self.suppress = suppress + + def begin(self, globalDir): + if self.suppress: + return + + # Create subdirectory, if needed + if self.customAbsDir: + absDir = self.customAbsDir + else: + absDir = os.path.join(globalDir, self.directory) + + filename = os.path.join(absDir, self.basename) + + self.file = open(filename + self.suffix, "w", encoding="utf-8") + self.file.write(self.preamble) + + def append(self, toAppend): + if self.suppress: + return + + self.file.write(toAppend) + + def end(self): + if self.suppress: + return + + self.file.write(self.postamble) + self.file.close() + + def getMakefileSrcEntry(self): + return "" + + def getCMakeSrcEntry(self): + return "" + +# Class capturing a .cpp file and a .h file (a "C++ module") + + +class Module(object): + + def __init__( + self, directory, basename, customAbsDir=None, suppress=False, implOnly=False, + headerOnly=False, suppressFeatureGuards=False): + self._headerFileModule = SingleFileModule( + ".h", directory, basename, customAbsDir, suppress or implOnly) + self._implFileModule = SingleFileModule( + ".cpp", directory, basename, customAbsDir, suppress or headerOnly) + + self._headerOnly = headerOnly + self._implOnly = implOnly + + self.directory = directory + self.basename = basename + self._customAbsDir = customAbsDir + + self.suppressFeatureGuards = suppressFeatureGuards + + @property + def suppress(self): + raise AttributeError("suppress is write only") + + @suppress.setter + def suppress(self, value: bool): + self._headerFileModule.suppress = self._implOnly or value + self._implFileModule.suppress = self._headerOnly or value + + @property + def headerPreamble(self) -> str: + return self._headerFileModule.preamble + + @headerPreamble.setter + def headerPreamble(self, value: str): + self._headerFileModule.preamble = value + + @property + def headerPostamble(self) -> str: + return self._headerFileModule.postamble + + @headerPostamble.setter + def headerPostamble(self, value: str): + self._headerFileModule.postamble = value + + @property + def implPreamble(self) -> str: + return self._implFileModule.preamble + + @implPreamble.setter + def implPreamble(self, value: str): + self._implFileModule.preamble = value + + @property + def implPostamble(self) -> str: + return self._implFileModule.postamble + + @implPostamble.setter + def implPostamble(self, value: str): + self._implFileModule.postamble = value + + def getMakefileSrcEntry(self): + if self._customAbsDir: + return self.basename + ".cpp \\\n" + dirName = self.directory + baseName = self.basename + joined = os.path.join(dirName, baseName) + return " " + joined + ".cpp \\\n" + + def getCMakeSrcEntry(self): + if self._customAbsDir: + return "\n" + self.basename + ".cpp " + dirName = Path(self.directory) + baseName = Path(self.basename) + joined = PurePosixPath(dirName / baseName) + return "\n " + str(joined) + ".cpp " + + def begin(self, globalDir): + self._headerFileModule.begin(globalDir) + self._implFileModule.begin(globalDir) + + def appendHeader(self, toAppend): + self._headerFileModule.append(toAppend) + + def appendImpl(self, toAppend): + self._implFileModule.append(toAppend) + + def end(self): + self._headerFileModule.end() + self._implFileModule.end() + + clang_format_command = shutil.which('clang-format') + assert (clang_format_command is not None) + + def formatFile(filename: Path): + assert (subprocess.call([clang_format_command, "-i", + "--style=file", str(filename.resolve())]) == 0) + + if not self._headerFileModule.suppress: + formatFile(Path(self._headerFileModule.file.name)) + + if not self._implFileModule.suppress: + formatFile(Path(self._implFileModule.file.name)) + + +class PyScript(SingleFileModule): + def __init__(self, directory, basename, customAbsDir=None, suppress=False): + super().__init__(".py", directory, basename, customAbsDir, suppress) + + +# Class capturing a .proto protobuf definition file +class Proto(SingleFileModule): + + def __init__(self, directory, basename, customAbsDir=None, suppress=False): + super().__init__(".proto", directory, basename, customAbsDir, suppress) + + def getMakefileSrcEntry(self): + super().getMakefileSrcEntry() + if self.customAbsDir: + return self.basename + ".proto \\\n" + dirName = self.directory + baseName = self.basename + joined = os.path.join(dirName, baseName) + return " " + joined + ".proto \\\n" + + def getCMakeSrcEntry(self): + super().getCMakeSrcEntry() + if self.customAbsDir: + return "\n" + self.basename + ".proto " + + dirName = self.directory + baseName = self.basename + joined = os.path.join(dirName, baseName) + return "\n " + joined + ".proto " + +class CodeGen(object): + + def __init__(self,): + self.code = "" + self.indentLevel = 0 + self.gensymCounter = [-1] + + def var(self, prefix="cgen_var"): + self.gensymCounter[-1] += 1 + res = "%s_%s" % (prefix, '_'.join(str(i) for i in self.gensymCounter if i >= 0)) + return res + + def swapCode(self,): + res = "%s" % self.code + self.code = "" + return res + + def indent(self,extra=0): + return "".join(" " * (self.indentLevel + extra)) + + def incrIndent(self,): + self.indentLevel += 1 + + def decrIndent(self,): + if self.indentLevel > 0: + self.indentLevel -= 1 + + def beginBlock(self, bracketPrint=True): + if bracketPrint: + self.code += self.indent() + "{\n" + self.indentLevel += 1 + self.gensymCounter.append(-1) + + def endBlock(self,bracketPrint=True): + self.indentLevel -= 1 + if bracketPrint: + self.code += self.indent() + "}\n" + del self.gensymCounter[-1] + + def beginIf(self, cond): + self.code += self.indent() + "if (" + cond + ")\n" + self.beginBlock() + + def beginElse(self, cond = None): + if cond is not None: + self.code += \ + self.indent() + \ + "else if (" + cond + ")\n" + else: + self.code += self.indent() + "else\n" + self.beginBlock() + + def endElse(self): + self.endBlock() + + def endIf(self): + self.endBlock() + + def beginSwitch(self, switchvar): + self.code += self.indent() + "switch (" + switchvar + ")\n" + self.beginBlock() + + def switchCase(self, switchval, blocked = False): + self.code += self.indent() + "case %s:" % switchval + self.beginBlock(bracketPrint = blocked) + + def switchCaseBreak(self, switchval, blocked = False): + self.code += self.indent() + "case %s:" % switchval + self.endBlock(bracketPrint = blocked) + + def switchCaseDefault(self, blocked = False): + self.code += self.indent() + "default:" % switchval + self.beginBlock(bracketPrint = blocked) + + def endSwitch(self): + self.endBlock() + + def beginWhile(self, cond): + self.code += self.indent() + "while (" + cond + ")\n" + self.beginBlock() + + def endWhile(self): + self.endBlock() + + def beginFor(self, initial, condition, increment): + self.code += \ + self.indent() + "for (" + \ + "; ".join([initial, condition, increment]) + \ + ")\n" + self.beginBlock() + + def endFor(self): + self.endBlock() + + def beginLoop(self, loopVarType, loopVar, loopInit, loopBound): + self.beginFor( + "%s %s = %s" % (loopVarType, loopVar, loopInit), + "%s < %s" % (loopVar, loopBound), + "++%s" % (loopVar)) + + def endLoop(self): + self.endBlock() + + def stmt(self, code): + self.code += self.indent() + code + ";\n" + + def line(self, code): + self.code += self.indent() + code + "\n" + + def leftline(self, code): + self.code += code + "\n" + + def makeCallExpr(self, funcName, parameters): + return funcName + "(%s)" % (", ".join(parameters)) + + def funcCall(self, lhs, funcName, parameters): + res = self.indent() + + if lhs is not None: + res += lhs + " = " + + res += self.makeCallExpr(funcName, parameters) + ";\n" + self.code += res + + def funcCallRet(self, _lhs, funcName, parameters): + res = self.indent() + res += "return " + self.makeCallExpr(funcName, parameters) + ";\n" + self.code += res + + # Given a VulkanType object, generate a C type declaration + # with optional parameter name: + # [const] [typename][*][const*] [paramName] + def makeCTypeDecl(self, vulkanType, useParamName=True): + constness = "const " if vulkanType.isConst else "" + typeName = vulkanType.typeName + + if vulkanType.pointerIndirectionLevels == 0: + ptrSpec = "" + elif vulkanType.isPointerToConstPointer: + ptrSpec = "* const*" if vulkanType.isConst else "**" + if vulkanType.pointerIndirectionLevels > 2: + ptrSpec += "*" * (vulkanType.pointerIndirectionLevels - 2) + else: + ptrSpec = "*" * vulkanType.pointerIndirectionLevels + + if useParamName and (vulkanType.paramName is not None): + paramStr = (" " + vulkanType.paramName) + else: + paramStr = "" + + return "%s%s%s%s" % (constness, typeName, ptrSpec, paramStr) + + def makeRichCTypeDecl(self, vulkanType, useParamName=True): + constness = "const " if vulkanType.isConst else "" + typeName = vulkanType.typeName + + if vulkanType.pointerIndirectionLevels == 0: + ptrSpec = "" + elif vulkanType.isPointerToConstPointer: + ptrSpec = "* const*" if vulkanType.isConst else "**" + if vulkanType.pointerIndirectionLevels > 2: + ptrSpec += "*" * (vulkanType.pointerIndirectionLevels - 2) + else: + ptrSpec = "*" * vulkanType.pointerIndirectionLevels + + if useParamName and (vulkanType.paramName is not None): + paramStr = (" " + vulkanType.paramName) + else: + paramStr = "" + + if vulkanType.staticArrExpr: + staticArrInfo = "[%s]" % vulkanType.staticArrExpr + else: + staticArrInfo = "" + + return "%s%s%s%s%s" % (constness, typeName, ptrSpec, paramStr, staticArrInfo) + + # Given a VulkanAPI object, generate the C function protype: + # () + def makeFuncProto(self, vulkanApi, useParamName=True): + + protoBegin = "%s %s" % (self.makeCTypeDecl( + vulkanApi.retType, useParamName=False), vulkanApi.name) + + def getFuncArgDecl(param): + if param.staticArrExpr: + return self.makeCTypeDecl(param, useParamName=useParamName) + ("[%s]" % param.staticArrExpr) + else: + return self.makeCTypeDecl(param, useParamName=useParamName) + + protoParams = "(\n %s)" % ((",\n%s" % self.indent(1)).join( + list(map( + getFuncArgDecl, + vulkanApi.parameters)))) + + return protoBegin + protoParams + + def makeFuncAlias(self, nameDst, nameSrc): + return "DEFINE_ALIAS_FUNCTION({}, {})\n\n".format(nameSrc, nameDst) + + def makeFuncDecl(self, vulkanApi): + return self.makeFuncProto(vulkanApi) + ";\n\n" + + def makeFuncImpl(self, vulkanApi, codegenFunc): + self.swapCode() + + self.line(self.makeFuncProto(vulkanApi)) + self.beginBlock() + codegenFunc(self) + self.endBlock() + + return self.swapCode() + "\n" + + def emitFuncImpl(self, vulkanApi, codegenFunc): + self.line(self.makeFuncProto(vulkanApi)) + self.beginBlock() + codegenFunc(self) + self.endBlock() + + def makeStructAccess(self, + vulkanType, + structVarName, + asPtr=True, + structAsPtr=True, + accessIndex=None): + + deref = "->" if structAsPtr else "." + + indexExpr = (" + %s" % accessIndex) if accessIndex else "" + + addrOfExpr = "" if vulkanType.accessibleAsPointer() or ( + not asPtr) else "&" + + return "%s%s%s%s%s" % (addrOfExpr, structVarName, deref, + vulkanType.paramName, indexExpr) + + def makeRawLengthAccess(self, vulkanType): + lenExpr = vulkanType.getLengthExpression() + + if not lenExpr: + return None, None + + if lenExpr == "null-terminated": + return "strlen(%s)" % vulkanType.paramName, None + + return lenExpr, None + + def makeLengthAccessFromStruct(self, + structInfo, + vulkanType, + structVarName, + asPtr=True): + # Handle special cases first + # Mostly when latexmath is involved + def handleSpecialCases(structInfo, vulkanType, structVarName, asPtr): + cases = [ + { + "structName": "VkShaderModuleCreateInfo", + "field": "pCode", + "lenExprMember": "codeSize", + "postprocess": lambda expr: "(%s / 4)" % expr + }, + { + "structName": "VkPipelineMultisampleStateCreateInfo", + "field": "pSampleMask", + "lenExprMember": "rasterizationSamples", + "postprocess": lambda expr: "(((%s) + 31) / 32)" % expr + }, + { + "structName": "VkAccelerationStructureVersionInfoKHR", + "field": "pVersionData", + "lenExprMember": "", + "postprocess": lambda _: "2*VK_UUID_SIZE" + }, + ] + + for c in cases: + if (structInfo.name, vulkanType.paramName) == (c["structName"], + c["field"]): + deref = "->" if asPtr else "." + expr = "%s%s%s" % (structVarName, deref, + c["lenExprMember"]) + lenAccessGuardExpr = "%s" % structVarName + return c["postprocess"](expr), lenAccessGuardExpr + + return None, None + + specialCaseAccess = \ + handleSpecialCases( + structInfo, vulkanType, structVarName, asPtr) + + if specialCaseAccess != (None, None): + return specialCaseAccess + + lenExpr = vulkanType.getLengthExpression() + + if not lenExpr: + return None, None + + deref = "->" if asPtr else "." + lenAccessGuardExpr = "%s" % ( + + structVarName) if deref else None + if lenExpr == "null-terminated": + return "strlen(%s%s%s)" % (structVarName, deref, + vulkanType.paramName), lenAccessGuardExpr + + if not structInfo.getMember(lenExpr): + return self.makeRawLengthAccess(vulkanType) + + return "%s%s%s" % (structVarName, deref, lenExpr), lenAccessGuardExpr + + def makeLengthAccessFromApi(self, api, vulkanType): + # Handle special cases first + # Mostly when :: is involved + def handleSpecialCases(vulkanType): + lenExpr = vulkanType.getLengthExpression() + + if lenExpr is None: + return None, None + + if "::" in lenExpr: + structVarName, memberVarName = lenExpr.split("::") + lenAccessGuardExpr = "%s" % (structVarName) + return "%s->%s" % (structVarName, memberVarName), lenAccessGuardExpr + return None, None + + specialCaseAccess = handleSpecialCases(vulkanType) + + if specialCaseAccess != (None, None): + return specialCaseAccess + + lenExpr = vulkanType.getLengthExpression() + + if not lenExpr: + return None, None + + lenExprInfo = api.getParameter(lenExpr) + + if not lenExprInfo: + return self.makeRawLengthAccess(vulkanType) + + if lenExpr == "null-terminated": + return "strlen(%s)" % vulkanType.paramName(), None + else: + deref = "*" if lenExprInfo.pointerIndirectionLevels > 0 else "" + lenAccessGuardExpr = "%s" % lenExpr if deref else None + return "(%s(%s))" % (deref, lenExpr), lenAccessGuardExpr + + def accessParameter(self, param, asPtr=True): + if asPtr: + if param.pointerIndirectionLevels > 0: + return param.paramName + else: + return "&%s" % param.paramName + else: + return param.paramName + + def sizeofExpr(self, vulkanType): + return "sizeof(%s)" % ( + self.makeCTypeDecl(vulkanType, useParamName=False)) + + def generalAccess(self, + vulkanType, + parentVarName=None, + asPtr=True, + structAsPtr=True): + if vulkanType.parent is None: + if parentVarName is None: + return self.accessParameter(vulkanType, asPtr=asPtr) + else: + return self.accessParameter(vulkanType.withModifiedName(parentVarName), asPtr=asPtr) + + if isinstance(vulkanType.parent, VulkanCompoundType): + return self.makeStructAccess( + vulkanType, parentVarName, asPtr=asPtr, structAsPtr=structAsPtr) + + if isinstance(vulkanType.parent, VulkanAPI): + if parentVarName is None: + return self.accessParameter(vulkanType, asPtr=asPtr) + else: + return self.accessParameter(vulkanType.withModifiedName(parentVarName), asPtr=asPtr) + + os.abort("Could not find a way to access Vulkan type %s" % + vulkanType.name) + + def makeLengthAccess(self, vulkanType, parentVarName="parent"): + if vulkanType.parent is None: + return self.makeRawLengthAccess(vulkanType) + + if isinstance(vulkanType.parent, VulkanCompoundType): + return self.makeLengthAccessFromStruct( + vulkanType.parent, vulkanType, parentVarName, asPtr=True) + + if isinstance(vulkanType.parent, VulkanAPI): + return self.makeLengthAccessFromApi(vulkanType.parent, vulkanType) + + os.abort("Could not find a way to access length of Vulkan type %s" % + vulkanType.name) + + def generalLengthAccess(self, vulkanType, parentVarName="parent"): + return self.makeLengthAccess(vulkanType, parentVarName)[0] + + def generalLengthAccessGuard(self, vulkanType, parentVarName="parent"): + return self.makeLengthAccess(vulkanType, parentVarName)[1] + + def vkApiCall(self, api, customPrefix="", globalStatePrefix="", customParameters=None, checkForDeviceLost=False, checkForOutOfMemory=False): + callLhs = None + + retTypeName = api.getRetTypeExpr() + retVar = None + + if retTypeName != "void": + retVar = api.getRetVarExpr() + self.stmt("%s %s = (%s)0" % (retTypeName, retVar, retTypeName)) + callLhs = retVar + + if customParameters is None: + self.funcCall( + callLhs, customPrefix + api.name, [p.paramName for p in api.parameters]) + else: + self.funcCall( + callLhs, customPrefix + api.name, customParameters) + + if retTypeName == "VkResult" and checkForDeviceLost: + self.stmt("if ((%s) == VK_ERROR_DEVICE_LOST) %sDeviceLost()" % (callLhs, globalStatePrefix)) + + if retTypeName == "VkResult" and checkForOutOfMemory: + if api.name == "vkAllocateMemory": + self.stmt( + "%sCheckOutOfMemory(%s, opcode, context, std::make_optional(pAllocateInfo->allocationSize))" + % (globalStatePrefix, callLhs)) + else: + self.stmt( + "%sCheckOutOfMemory(%s, opcode, context)" + % (globalStatePrefix, callLhs)) + + return (retTypeName, retVar) + + def makeCheckVkSuccess(self, expr): + return "((%s) == VK_SUCCESS)" % expr + + def makeReinterpretCast(self, varName, typeName, const=True): + return "reinterpret_cast<%s%s*>(%s)" % \ + ("const " if const else "", typeName, varName) + + def validPrimitive(self, typeInfo, typeName): + size = typeInfo.getPrimitiveEncodingSize(typeName) + return size != None + + def makePrimitiveStreamMethod(self, typeInfo, typeName, direction="write"): + if not self.validPrimitive(typeInfo, typeName): + return None + + size = typeInfo.getPrimitiveEncodingSize(typeName) + prefix = "put" if direction == "write" else "get" + suffix = None + if size == 1: + suffix = "Byte" + elif size == 2: + suffix = "Be16" + elif size == 4: + suffix = "Be32" + elif size == 8: + suffix = "Be64" + + if suffix: + return prefix + suffix + + return None + + def makePrimitiveStreamMethodInPlace(self, typeInfo, typeName, direction="write"): + if not self.validPrimitive(typeInfo, typeName): + return None + + size = typeInfo.getPrimitiveEncodingSize(typeName) + prefix = "to" if direction == "write" else "from" + suffix = None + if size == 1: + suffix = "Byte" + elif size == 2: + suffix = "Be16" + elif size == 4: + suffix = "Be32" + elif size == 8: + suffix = "Be64" + + if suffix: + return prefix + suffix + + return None + + def streamPrimitive(self, typeInfo, streamVar, accessExpr, accessType, direction="write"): + accessTypeName = accessType.typeName + + if accessType.pointerIndirectionLevels == 0 and not self.validPrimitive(typeInfo, accessTypeName): + print("Tried to stream a non-primitive type: %s" % accessTypeName) + os.abort() + + needPtrCast = False + + if accessType.pointerIndirectionLevels > 0: + streamSize = 8 + streamStorageVarType = "uint64_t" + needPtrCast = True + streamMethod = "putBe64" if direction == "write" else "getBe64" + else: + streamSize = typeInfo.getPrimitiveEncodingSize(accessTypeName) + if streamSize == 1: + streamStorageVarType = "uint8_t" + elif streamSize == 2: + streamStorageVarType = "uint16_t" + elif streamSize == 4: + streamStorageVarType = "uint32_t" + elif streamSize == 8: + streamStorageVarType = "uint64_t" + streamMethod = self.makePrimitiveStreamMethod( + typeInfo, accessTypeName, direction=direction) + + streamStorageVar = self.var() + + accessCast = self.makeRichCTypeDecl(accessType, useParamName=False) + + ptrCast = "(uintptr_t)" if needPtrCast else "" + + if direction == "read": + self.stmt("%s = (%s)%s%s->%s()" % + (accessExpr, + accessCast, + ptrCast, + streamVar, + streamMethod)) + else: + self.stmt("%s %s = (%s)%s%s" % + (streamStorageVarType, streamStorageVar, + streamStorageVarType, ptrCast, accessExpr)) + self.stmt("%s->%s(%s)" % + (streamVar, streamMethod, streamStorageVar)) + + def memcpyPrimitive(self, typeInfo, streamVar, accessExpr, accessType, variant, direction="write"): + accessTypeName = accessType.typeName + + if accessType.pointerIndirectionLevels == 0 and not self.validPrimitive(typeInfo, accessTypeName): + print("Tried to stream a non-primitive type: %s" % accessTypeName) + os.abort() + + needPtrCast = False + + streamSize = 8 + + if accessType.pointerIndirectionLevels > 0: + streamSize = 8 + streamStorageVarType = "uint64_t" + needPtrCast = True + streamMethod = "toBe64" if direction == "write" else "fromBe64" + else: + streamSize = typeInfo.getPrimitiveEncodingSize(accessTypeName) + if streamSize == 1: + streamStorageVarType = "uint8_t" + elif streamSize == 2: + streamStorageVarType = "uint16_t" + elif streamSize == 4: + streamStorageVarType = "uint32_t" + elif streamSize == 8: + streamStorageVarType = "uint64_t" + streamMethod = self.makePrimitiveStreamMethodInPlace( + typeInfo, accessTypeName, direction=direction) + + streamStorageVar = self.var() + + accessCast = self.makeRichCTypeDecl(accessType, useParamName=False) + + if direction == "read": + accessCast = self.makeRichCTypeDecl( + accessType.getForNonConstAccess(), useParamName=False) + + ptrCast = "(uintptr_t)" if needPtrCast else "" + + streamNamespace = "gfxstream::guest" if variant == "guest" else "android::base" + + if direction == "read": + self.stmt("memcpy((%s*)&%s, %s, %s)" % + (accessCast, + accessExpr, + streamVar, + str(streamSize))) + self.stmt("%s::Stream::%s((uint8_t*)&%s)" % ( + streamNamespace, + streamMethod, + accessExpr)) + else: + self.stmt("%s %s = (%s)%s%s" % + (streamStorageVarType, streamStorageVar, + streamStorageVarType, ptrCast, accessExpr)) + self.stmt("memcpy(%s, &%s, %s)" % + (streamVar, streamStorageVar, str(streamSize))) + self.stmt("%s::Stream::%s((uint8_t*)%s)" % ( + streamNamespace, + streamMethod, + streamVar)) + + def countPrimitive(self, typeInfo, accessType): + accessTypeName = accessType.typeName + + if accessType.pointerIndirectionLevels == 0 and not self.validPrimitive(typeInfo, accessTypeName): + print("Tried to count a non-primitive type: %s" % accessTypeName) + os.abort() + + needPtrCast = False + + if accessType.pointerIndirectionLevels > 0: + streamSize = 8 + else: + streamSize = typeInfo.getPrimitiveEncodingSize(accessTypeName) + + return streamSize + +# Class to wrap a Vulkan API call. +# +# The user gives a generic callback, |codegenDef|, +# that takes a CodeGen object and a VulkanAPI object as arguments. +# codegenDef uses CodeGen along with the VulkanAPI object +# to generate the function body. +class VulkanAPIWrapper(object): + + def __init__(self, + customApiPrefix, + extraParameters=None, + returnTypeOverride=None, + codegenDef=None): + self.customApiPrefix = customApiPrefix + self.extraParameters = extraParameters + self.returnTypeOverride = returnTypeOverride + + self.codegen = CodeGen() + + self.definitionFunc = codegenDef + + # Private function + + def makeApiFunc(self, typeInfo, apiName): + customApi = copy(typeInfo.apis[apiName]) + customApi.name = self.customApiPrefix + customApi.name + if self.extraParameters is not None: + if isinstance(self.extraParameters, list): + customApi.parameters = \ + self.extraParameters + customApi.parameters + else: + os.abort( + "Type of extra parameters to custom API not valid. Expected list, got %s" % type( + self.extraParameters)) + + if self.returnTypeOverride is not None: + customApi.retType = self.returnTypeOverride + return customApi + + self.makeApi = makeApiFunc + + def setCodegenDef(self, codegenDefFunc): + self.definitionFunc = codegenDefFunc + + def makeDecl(self, typeInfo, apiName): + return self.codegen.makeFuncProto( + self.makeApi(self, typeInfo, apiName)) + ";\n\n" + + def makeDefinition(self, typeInfo, apiName, isStatic=False): + vulkanApi = self.makeApi(self, typeInfo, apiName) + + self.codegen.swapCode() + self.codegen.beginBlock() + + if self.definitionFunc is None: + print("ERROR: No definition found for (%s, %s)" % + (vulkanApi.name, self.customApiPrefix)) + sys.exit(1) + + self.definitionFunc(self.codegen, vulkanApi) + + self.codegen.endBlock() + + return ("static " if isStatic else "") + self.codegen.makeFuncProto( + vulkanApi) + "\n" + self.codegen.swapCode() + "\n" + +# Base class for wrapping all Vulkan API objects. These work with Vulkan +# Registry generators and have gen* triggers. They tend to contain +# VulkanAPIWrapper objects to make it easier to generate the code. +class VulkanWrapperGenerator(object): + + def __init__(self, module: Module, typeInfo: VulkanTypeInfo): + self.module: Module = module + self.typeInfo: VulkanTypeInfo = typeInfo + self.extensionStructTypes = OrderedDict() + + def onBegin(self): + pass + + def onEnd(self): + pass + + def onBeginFeature(self, featureName, featureType): + pass + + def onFeatureNewCmd(self, cmdName): + pass + + def onEndFeature(self): + pass + + def onGenType(self, typeInfo, name, alias): + category = self.typeInfo.categoryOf(name) + if category in ["struct", "union"] and not alias: + structInfo = self.typeInfo.structs[name] + if structInfo.structExtendsExpr: + self.extensionStructTypes[name] = structInfo + pass + + def onGenStruct(self, typeInfo, name, alias): + pass + + def onGenGroup(self, groupinfo, groupName, alias=None): + pass + + def onGenEnum(self, enuminfo, name, alias): + pass + + def onGenCmd(self, cmdinfo, name, alias): + pass + + # Below Vulkan structure types may correspond to multiple Vulkan structs + # due to a conflict between different Vulkan registries. In order to get + # the correct Vulkan struct type, we need to check the type of its "root" + # struct as well. + ROOT_TYPE_MAPPING = { + "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT": { + "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2": "VkPhysicalDeviceFragmentDensityMapFeaturesEXT", + "VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO": "VkPhysicalDeviceFragmentDensityMapFeaturesEXT", + "VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO": "VkImportColorBufferGOOGLE", + "default": "VkPhysicalDeviceFragmentDensityMapFeaturesEXT", + }, + "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT": { + "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2": "VkPhysicalDeviceFragmentDensityMapPropertiesEXT", + "VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO": "VkCreateBlobGOOGLE", + "default": "VkPhysicalDeviceFragmentDensityMapPropertiesEXT", + }, + "VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT": { + "VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO": "VkRenderPassFragmentDensityMapCreateInfoEXT", + "VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2": "VkRenderPassFragmentDensityMapCreateInfoEXT", + "VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO": "VkImportBufferGOOGLE", + "default": "VkRenderPassFragmentDensityMapCreateInfoEXT", + }, + } + + def emitForEachStructExtension(self, cgen, retType, triggerVar, forEachFunc, autoBreak=True, defaultEmit=None, nullEmit=None, rootTypeVar=None): + def readStructType(structTypeName, structVarName, cgen): + cgen.stmt("uint32_t %s = (uint32_t)%s(%s)" % \ + (structTypeName, "goldfish_vk_struct_type", structVarName)) + + def castAsStruct(varName, typeName, const=True): + return "reinterpret_cast<%s%s*>(%s)" % \ + ("const " if const else "", typeName, varName) + + def doDefaultReturn(cgen): + if retType.typeName == "void": + cgen.stmt("return") + else: + cgen.stmt("return (%s)0" % retType.typeName) + + cgen.beginIf("!%s" % triggerVar.paramName) + if nullEmit is None: + doDefaultReturn(cgen) + else: + nullEmit(cgen) + cgen.endIf() + + readStructType("structType", triggerVar.paramName, cgen) + + cgen.line("switch(structType)") + cgen.beginBlock() + + currFeature = None + + for ext in self.extensionStructTypes.values(): + if not currFeature: + cgen.leftline("#ifdef %s" % ext.feature) + currFeature = ext.feature + + if currFeature and ext.feature != currFeature: + cgen.leftline("#endif") + cgen.leftline("#ifdef %s" % ext.feature) + currFeature = ext.feature + + enum = ext.structEnumExpr + protect = None + if enum in self.typeInfo.enumElem: + protect = self.typeInfo.enumElem[enum].get("protect", default=None) + if protect is not None: + cgen.leftline("#ifdef %s" % protect) + + cgen.line("case %s:" % enum) + cgen.beginBlock() + + if rootTypeVar is not None and enum in VulkanWrapperGenerator.ROOT_TYPE_MAPPING: + cgen.line("switch(%s)" % rootTypeVar.paramName) + cgen.beginBlock() + kv = VulkanWrapperGenerator.ROOT_TYPE_MAPPING[enum] + for k in kv: + v = self.extensionStructTypes[kv[k]] + if k == "default": + cgen.line("%s:" % k) + else: + cgen.line("case %s:" % k) + cgen.beginBlock() + castedAccess = castAsStruct( + triggerVar.paramName, v.name, const=triggerVar.isConst) + forEachFunc(v, castedAccess, cgen) + cgen.line("break;") + cgen.endBlock() + cgen.endBlock() + else: + castedAccess = castAsStruct( + triggerVar.paramName, ext.name, const=triggerVar.isConst) + forEachFunc(ext, castedAccess, cgen) + + if autoBreak: + cgen.stmt("break") + cgen.endBlock() + + if protect is not None: + cgen.leftline("#endif // %s" % protect) + + if currFeature: + cgen.leftline("#endif") + + cgen.line("default:") + cgen.beginBlock() + if defaultEmit is None: + doDefaultReturn(cgen) + else: + defaultEmit(cgen) + cgen.endBlock() + + cgen.endBlock() + + def emitForEachStructExtensionGeneral(self, cgen, forEachFunc, doFeatureIfdefs=False): + currFeature = None + + for (i, ext) in enumerate(self.extensionStructTypes.values()): + if doFeatureIfdefs: + if not currFeature: + cgen.leftline("#ifdef %s" % ext.feature) + currFeature = ext.feature + + if currFeature and ext.feature != currFeature: + cgen.leftline("#endif") + cgen.leftline("#ifdef %s" % ext.feature) + currFeature = ext.feature + + forEachFunc(i, ext, cgen) + + if doFeatureIfdefs: + if currFeature: + cgen.leftline("#endif") diff --git a/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py b/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py new file mode 100644 index 00000000000..728f23d2b4e --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/common/vulkantypes.py @@ -0,0 +1,1324 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import Dict, Optional, List, Set, Union +from xml.etree.ElementTree import Element + +from generator import noneStr + +from copy import copy +from dataclasses import dataclass +from string import whitespace + +# Holds information about core Vulkan objects +# and the API calls that are used to create/destroy each one. +class HandleInfo(object): + def __init__(self, name, createApis, destroyApis): + self.name = name + self.createApis = createApis + self.destroyApis = destroyApis + + def isCreateApi(self, apiName): + return apiName == self.createApis or (apiName in self.createApis) + + def isDestroyApi(self, apiName): + if self.destroyApis is None: + return False + return apiName == self.destroyApis or (apiName in self.destroyApis) + +DISPATCHABLE_HANDLE_TYPES = [ + "VkInstance", + "VkPhysicalDevice", + "VkDevice", + "VkQueue", + "VkCommandBuffer", +] + +NON_DISPATCHABLE_HANDLE_TYPES = [ + "VkDeviceMemory", + "VkBuffer", + "VkBufferView", + "VkImage", + "VkImageView", + "VkShaderModule", + "VkDescriptorPool", + "VkDescriptorSetLayout", + "VkDescriptorSet", + "VkSampler", + "VkPipeline", + "VkPipelineLayout", + "VkRenderPass", + "VkFramebuffer", + "VkPipelineCache", + "VkCommandPool", + "VkFence", + "VkSemaphore", + "VkEvent", + "VkQueryPool", + "VkSamplerYcbcrConversion", + "VkSamplerYcbcrConversionKHR", + "VkDescriptorUpdateTemplate", + "VkSurfaceKHR", + "VkSwapchainKHR", + "VkDisplayKHR", + "VkDisplayModeKHR", + "VkObjectTableNVX", + "VkIndirectCommandsLayoutNVX", + "VkValidationCacheEXT", + "VkDebugReportCallbackEXT", + "VkDebugUtilsMessengerEXT", + "VkAccelerationStructureNV", + "VkIndirectCommandsLayoutNV", + "VkAccelerationStructureKHR", +] + +CUSTOM_HANDLE_CREATE_TYPES = [ + "VkPhysicalDevice", + "VkQueue", + "VkPipeline", + "VkDeviceMemory", + "VkDescriptorSet", + "VkCommandBuffer", + "VkRenderPass", +] + +HANDLE_TYPES = list(sorted(list(set(DISPATCHABLE_HANDLE_TYPES + + NON_DISPATCHABLE_HANDLE_TYPES + CUSTOM_HANDLE_CREATE_TYPES)))) + +HANDLE_INFO = {} + +for h in HANDLE_TYPES: + if h in CUSTOM_HANDLE_CREATE_TYPES: + if h == "VkPhysicalDevice": + HANDLE_INFO[h] = \ + HandleInfo( + "VkPhysicalDevice", + "vkEnumeratePhysicalDevices", None) + if h == "VkQueue": + HANDLE_INFO[h] = \ + HandleInfo( + "VkQueue", + ["vkGetDeviceQueue", "vkGetDeviceQueue2"], + None) + if h == "VkPipeline": + HANDLE_INFO[h] = \ + HandleInfo( + "VkPipeline", + ["vkCreateGraphicsPipelines", "vkCreateComputePipelines"], + "vkDestroyPipeline") + if h == "VkDeviceMemory": + HANDLE_INFO[h] = \ + HandleInfo("VkDeviceMemory", + "vkAllocateMemory", ["vkFreeMemory", "vkFreeMemorySyncGOOGLE"]) + if h == "VkDescriptorSet": + HANDLE_INFO[h] = \ + HandleInfo("VkDescriptorSet", "vkAllocateDescriptorSets", + "vkFreeDescriptorSets") + if h == "VkCommandBuffer": + HANDLE_INFO[h] = \ + HandleInfo("VkCommandBuffer", "vkAllocateCommandBuffers", + "vkFreeCommandBuffers") + if h == "VkRenderPass": + HANDLE_INFO[h] = \ + HandleInfo( + "VkRenderPass", + ["vkCreateRenderPass", "vkCreateRenderPass2", "vkCreateRenderPass2KHR"], + "vkDestroyRenderPass") + else: + HANDLE_INFO[h] = \ + HandleInfo(h, "vkCreate" + h[2:], "vkDestroy" + h[2:]) + +EXCLUDED_APIS = [ + "vkEnumeratePhysicalDeviceGroups", +] + +EXPLICITLY_ABI_PORTABLE_TYPES = [ + "VkResult", + "VkBool32", + "VkSampleMask", + "VkFlags", + "VkDeviceSize", +] + +EXPLICITLY_ABI_NON_PORTABLE_TYPES = [ + "size_t" +] + +NON_ABI_PORTABLE_TYPE_CATEGORIES = [ + "handle", + "funcpointer", +] + +# A class for holding the parameter indices corresponding to various +# attributes about a VkDeviceMemory, such as the handle, size, offset, etc. +@dataclass +class DeviceMemoryInfoParameterIndices: + handle: int = -1 + offset: int = -1 + size: int = -1 + typeIndex: int = -1 + typeBits: int = -1 + +DEVICE_MEMORY_STRUCTS = { + "VkMemoryAllocateInfo": {"1": DeviceMemoryInfoParameterIndices(typeIndex = 3)}, + "VkMemoryRequirements": {"1": DeviceMemoryInfoParameterIndices(typeBits = 2)}, + "VkMappedMemoryRange": {"1": DeviceMemoryInfoParameterIndices(handle = 2, offset = 3, size = 4)}, + "VkSparseMemoryBind": {"1": DeviceMemoryInfoParameterIndices(handle = 2, offset = 3)}, + "VkSparseImageMemoryBind": {"1": DeviceMemoryInfoParameterIndices(handle = 3, offset = 4)}, + "VkWin32KeyedMutexAcquireReleaseInfoNV": {"1": DeviceMemoryInfoParameterIndices(handle = 3), "2": DeviceMemoryInfoParameterIndices(handle = 7)}, + "VkMemoryWin32HandlePropertiesKHR": {"1": DeviceMemoryInfoParameterIndices(typeBits = 2)}, + "VkMemoryGetWin32HandleInfoKHR": {"1": DeviceMemoryInfoParameterIndices(handle = 2)}, + "VkMemoryFdPropertiesKHR": {"1": DeviceMemoryInfoParameterIndices(typeBits = 2)}, + "VkMemoryGetFdInfoKHR": {"1": DeviceMemoryInfoParameterIndices(handle = 2)}, + "VkWin32KeyedMutexAcquireReleaseInfoKHR": {"1": DeviceMemoryInfoParameterIndices(handle = 3), "2": DeviceMemoryInfoParameterIndices(handle = 7)}, + "VkBindBufferMemoryInfo": {"1": DeviceMemoryInfoParameterIndices(handle = 3, offset = 4)}, + "VkBindImageMemoryInfo": {"1": DeviceMemoryInfoParameterIndices(handle = 3, offset = 4)}, + "VkMemoryHostPointerPropertiesEXT": {"1": DeviceMemoryInfoParameterIndices(typeBits = 2)}, + "VkAndroidHardwareBufferPropertiesANDROID": {"1": DeviceMemoryInfoParameterIndices(typeBits = 3)}, + "VkMemoryGetAndroidHardwareBufferInfoANDROID": {"1": DeviceMemoryInfoParameterIndices(handle = 2)}, + "VkBindAccelerationStructureMemoryInfoNV": {"1": DeviceMemoryInfoParameterIndices(handle = 3, offset = 4)}, + "VkDeviceMemoryOpaqueCaptureAddressInfo": {"1": DeviceMemoryInfoParameterIndices(handle = 2)}, +} + +DEVICE_MEMORY_COMMANDS = { + "vkFreeMemory": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkMapMemory": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkUnmapMemory": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkGetDeviceMemoryCommitment": {"1": DeviceMemoryInfoParameterIndices(handle = 1, offset = 2)}, + "vkBindBufferMemory": {"1": DeviceMemoryInfoParameterIndices(handle = 2, offset = 3)}, + "vkBindImageMemory": {"1": DeviceMemoryInfoParameterIndices(handle = 2, offset = 3)}, + "vkGetBlobGOOGLE": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkGetMemoryWin32HandleNV": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkMapMemoryIntoAddressSpaceGOOGLE": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkGetMemoryHostAddressInfoGOOGLE": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, + "vkFreeMemorySyncGOOGLE": {"1": DeviceMemoryInfoParameterIndices(handle = 1)}, +} + +TRIVIAL_TRANSFORMED_TYPES = [ + "VkPhysicalDeviceExternalImageFormatInfo", + "VkPhysicalDeviceExternalBufferInfo", + "VkExternalMemoryImageCreateInfo", + "VkExternalMemoryBufferCreateInfo", + "VkExportMemoryAllocateInfo", + "VkExternalImageFormatProperties", + "VkExternalBufferProperties", +] + +NON_TRIVIAL_TRANSFORMED_TYPES = [ + "VkExternalMemoryProperties", + "VkImageCreateInfo", +] + +TRANSFORMED_TYPES = TRIVIAL_TRANSFORMED_TYPES + NON_TRIVIAL_TRANSFORMED_TYPES + +STRUCT_STREAM_FEATURE = { + "VkPhysicalDeviceShaderFloat16Int8Features": "VULKAN_STREAM_FEATURE_SHADER_FLOAT16_INT8_BIT", + "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR": "VULKAN_STREAM_FEATURE_SHADER_FLOAT16_INT8_BIT", + "VkPhysicalDeviceFloat16Int8FeaturesKHR": "VULKAN_STREAM_FEATURE_SHADER_FLOAT16_INT8_BIT", +} + +STRUCT_MEMBER_STREAM_FEATURE = { + "VkGraphicsPipelineCreateInfo.pVertexInputState": "VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT", + "VkGraphicsPipelineCreateInfo.pInputAssemblyState": "VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT", + "VkGraphicsPipelineCreateInfo.pRasterizationState": "VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT", +} + +STRUCT_ENV_STR = { + "VkGraphicsPipelineCreateInfo": { + "hasTessellation": "(arrayany pStages 0 stageCount (lambda ((s VkPipelineShaderStageCreateInfo)) (or (eq (getfield s stage) VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) (eq (getfield s stage) VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))))", + "hasRasterization" : "(or (if (eq 0 pRasterizationState) 0 (not (getfield pRasterizationState rasterizerDiscardEnable))) (if (eq 0 pDynamicState) 0 (arrayany (getfield pDynamicState pDynamicStates) 0 (getfield pDynamicState dynamicStateCount) (lambda ((s VkDynamicState)) (eq s VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE)))))" + }, +} + +STRUCT_MEMBER_FILTER_VAR = { + "VkGraphicsPipelineCreateInfo.pTessellationState": "hasTessellation", + "VkGraphicsPipelineCreateInfo.pViewportState": "hasRasterization", + "VkGraphicsPipelineCreateInfo.pMultisampleState": "hasRasterization", + "VkGraphicsPipelineCreateInfo.pDepthStencilState": "hasRasterization", + "VkGraphicsPipelineCreateInfo.pColorBlendState": "hasRasterization", + "VkWriteDescriptorSet.pImageInfo": "descriptorType", + "VkWriteDescriptorSet.pBufferInfo": "descriptorType", + "VkWriteDescriptorSet.pTexelBufferView": "descriptorType", + "VkFramebufferCreateInfo.pAttachments": "flags", +} + +STRUCT_MEMBER_FILTER_VALS = { + "VkWriteDescriptorSet.pImageInfo": [ + "VK_DESCRIPTOR_TYPE_SAMPLER", + "VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER", + "VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE", + "VK_DESCRIPTOR_TYPE_STORAGE_IMAGE", + "VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT" + ], + "VkWriteDescriptorSet.pBufferInfo": [ + "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER", + "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC", + "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER", + "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC", + ], + "VkWriteDescriptorSet.pTexelBufferView": [ + "VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER", + "VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER", + ], +} + +STRUCT_MEMBER_FILTER_FUNC = { + "VkFramebufferCreateInfo.pAttachments": "(eq (bitwise_and flags VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) 0)", +} + +# vk.xml added optional to some of the existing fields. For backward compatibility +# we need to ignore those optionals. +# We might want to add more complex safety checks in future. +STRUCT_MEMBER_IGNORE_OPTIONAL = { + "VkSubmitInfo.pWaitDstStageMask", + "VkPipelineLayoutCreateInfo.pSetLayouts", + "VkGraphicsPipelineCreateInfo.pStages", + "VkPipelineColorBlendStateCreateInfo.pAttachments", + "VkFramebufferCreateInfo.attachmentCount", + "VkFramebufferCreateInfo.pAttachments", + "VkVideoProfileInfoKHR.chromaBitDepth", + "VkVideoDecodeInfoKHR.pSetupReferenceSlot", + "vkCmdBindDescriptorSets.pDescriptorSets", + "vkCmdBindDescriptorSets.local_pDescriptorSets", + "vkCmdBindVertexBuffers.pBuffers", + "vkCmdBindVertexBuffers.local_pBuffers", + "vkCmdClearColorImage.pColor", + "vkCmdClearColorImage.local_pColor", +} + +# Holds information about a Vulkan type instance (i.e., not a type definition). +# Type instances are used as struct field definitions or function parameters, +# to be later fed to code generation. +# VulkanType instances can be constructed in two ways: +# 1. From an XML tag with / tags in vk.xml, +# using makeVulkanTypeFromXMLTag +# 2. User-defined instances with makeVulkanTypeSimple. +class VulkanType(object): + + def __init__(self): + self.parent: Optional[VulkanType] = None + self.typeName: str = "" + + self.isTransformed = False + + self.paramName: Optional[str] = None + + self.lenExpr: Optional[str] = None # Value of the `len` attribute in the spec + self.isOptional: bool = False + self.optionalStr: Optional[str] = None # Value of the `optional` attribute in the spec + + self.isConst = False + + # "" means it's not a static array, otherwise this is the total size of + # all elements. e.g. staticArrExpr of "x[3][2][8]" will be "((3)*(2)*(8))". + self.staticArrExpr = "" + # "" means it's not a static array, otherwise it's the raw expression + # of static array size, which can be one-dimensional or multi-dimensional. + self.rawStaticArrExpr = "" + + self.pointerIndirectionLevels = 0 # 0 means not pointer + self.isPointerToConstPointer = False + + self.primitiveEncodingSize = None + + self.deviceMemoryInfoParameterIndices = None + + # Annotations + # Environment annotation for binding current + # variables to sub-structures + self.binds = {} + + # Device memory annotations + + # self.deviceMemoryAttrib/Val stores + # device memory info attributes + self.deviceMemoryAttrib = None + self.deviceMemoryVal = None + + # Filter annotations + self.filterVar = None + self.filterVals = None + self.filterFunc = None + self.filterOtherwise = None + + # Stream feature + self.streamFeature = None + + # All other annotations + self.attribs = {} + + self.nonDispatchableHandleCreate = False + self.nonDispatchableHandleDestroy = False + self.dispatchHandle = False + self.dispatchableHandleCreate = False + self.dispatchableHandleDestroy = False + + + def __str__(self,): + return ("(vulkantype %s %s paramName %s len %s optional? %s " + "staticArrExpr %s)") % ( + self.typeName + ("*" * self.pointerIndirectionLevels) + + ("ptr2constptr" if self.isPointerToConstPointer else ""), "const" + if self.isConst else "nonconst", self.paramName, self.lenExpr, + self.isOptional, self.staticArrExpr) + + def isString(self): + return self.pointerIndirectionLevels == 1 and (self.typeName == "char") + + def isArrayOfStrings(self): + return self.isPointerToConstPointer and (self.typeName == "char") + + def primEncodingSize(self): + return self.primitiveEncodingSize + + # Utility functions to make codegen life easier. + # This method derives the correct "count" expression if possible. + # Otherwise, returns None or "null-terminated" if a string. + def getLengthExpression(self): + if self.staticArrExpr != "": + return self.staticArrExpr + if self.lenExpr: + # Use a simple lookup table for latexmath. + known_expressions = { + r"latexmath:[\lceil{\mathit{samples} \over 32}\rceil]": + "int(samples / 32)", + r"latexmath:[2 \times \mathtt{VK\_UUID\_SIZE}]": "2 * VK_UUID_SIZE", + } + if self.lenExpr in known_expressions: + return known_expressions[self.lenExpr] + return self.lenExpr + return None + + # Can we just pass this to functions expecting T* + def accessibleAsPointer(self): + if self.staticArrExpr != "": + return True + if self.pointerIndirectionLevels > 0: + return True + return False + + # Rough attempt to infer where a type could be an output. + # Good for inferring which things need to be marshaled in + # versus marshaled out for Vulkan API calls + def possiblyOutput(self,): + return self.pointerIndirectionLevels > 0 and (not self.isConst) + + def isVoidWithNoSize(self,): + return self.typeName == "void" and self.pointerIndirectionLevels == 0 + + def getCopy(self,): + return copy(self) + + def getTransformed(self, isConstChoice=None, ptrIndirectionChoice=None): + res = self.getCopy() + + if isConstChoice is not None: + res.isConst = isConstChoice + if ptrIndirectionChoice is not None: + res.pointerIndirectionLevels = ptrIndirectionChoice + + return res + + def getWithCustomName(self): + return self.getTransformed( + ptrIndirectionChoice=self.pointerIndirectionLevels + 1) + + def getForAddressAccess(self): + return self.getTransformed( + ptrIndirectionChoice=self.pointerIndirectionLevels + 1) + + def getForValueAccess(self): + if self.typeName == "void" and self.pointerIndirectionLevels == 1: + asUint8Type = self.getCopy() + asUint8Type.typeName = "uint8_t" + return asUint8Type.getForValueAccess() + return self.getTransformed( + ptrIndirectionChoice=self.pointerIndirectionLevels - 1) + + def getForNonConstAccess(self): + return self.getTransformed(isConstChoice=False) + + def withModifiedName(self, newName): + res = self.getCopy() + res.paramName = newName + return res + + def isNextPointer(self): + return self.paramName == "pNext" + + def isSigned(self): + return self.typeName in ["int", "int8_t", "int16_t", "int32_t", "int64_t"] + + def isEnum(self, typeInfo): + return typeInfo.categoryOf(self.typeName) == "enum" + + def isBitmask(self, typeInfo): + return typeInfo.categoryOf(self.typeName) == "enum" + + # Only deals with 'core' handle types here. + def isDispatchableHandleType(self): + return self.typeName in DISPATCHABLE_HANDLE_TYPES + + def isNonDispatchableHandleType(self): + return self.typeName in NON_DISPATCHABLE_HANDLE_TYPES + + def isHandleType(self): + return self.isDispatchableHandleType() or \ + self.isNonDispatchableHandleType() + + def isCreatedBy(self, api): + if self.shouldSkip(): + return False + if self.typeName in HANDLE_INFO.keys(): + nonKhrRes = HANDLE_INFO[self.typeName].isCreateApi(api.name) + if nonKhrRes: + return True + if len(api.name) > 3 and "KHR" == api.name[-3:]: + return HANDLE_INFO[self.typeName].isCreateApi(api.name[:-3]) + + if self.typeName == "VkImage" and api.name == "vkCreateImageWithRequirementsGOOGLE": + return True + + if self.typeName == "VkBuffer" and api.name == "vkCreateBufferWithRequirementsGOOGLE": + return True + + return False + + def isDestroyedBy(self, api): + if self.shouldSkip(): + return False + if self.typeName in HANDLE_INFO.keys(): + nonKhrRes = HANDLE_INFO[self.typeName].isDestroyApi(api.name) + if nonKhrRes: + return True + if len(api.name) > 3 and "KHR" == api.name[-3:]: + return HANDLE_INFO[self.typeName].isDestroyApi(api.name[:-3]) + + return False + + def isSimpleValueType(self, typeInfo): + if typeInfo.isCompoundType(self.typeName): + return False + if self.isString() or self.isArrayOfStrings(): + return False + if self.staticArrExpr or self.pointerIndirectionLevels > 0: + return False + return True + + def getStructEnumExpr(self,): + return None + + def getPrintFormatSpecifier(self): + kKnownTypePrintFormatSpecifiers = { + 'float': '%f', + 'int': '%d', + 'int32_t': '%d', + 'size_t': '%ld', + 'uint16_t': '%d', + 'uint32_t': '%d', + 'uint64_t': '%ld', + 'VkBool32': '%d', + 'VkDeviceSize': '%ld', + 'VkFormat': '%d', + 'VkImageLayout': '%d', + } + + if self.pointerIndirectionLevels > 0 or self.isHandleType(): + return '%p' + + if self.typeName in kKnownTypePrintFormatSpecifiers: + return kKnownTypePrintFormatSpecifiers[self.typeName] + + if self.typeName.endswith('Flags'): + # Based on `typedef uint32_t VkFlags;` + return '%d' + + return None + def isOptionalPointer(self) -> bool: + return self.isOptional and \ + (not self.isForceOptional()) and\ + self.pointerIndirectionLevels > 0 and \ + (not self.isNextPointer()) + + def isForceOptional(self) -> bool: + """ + Returns true if we should generate a placeholder for null. + + Vulkan updates change certain pointers from non-optional to + optional. We want to keep our encoder/decoder backward compatible. + Thus we should generate a placeholder for such APIs. + """ + return self.getFullName() in STRUCT_MEMBER_IGNORE_OPTIONAL + + def getFullName(self) -> str: + if self.parent is None: + return self.paramName + return f"{self.parent.name}.{self.paramName}" + + def getProtectStreamFeature(self) -> Optional[str]: + key = self.getFullName() + if key in STRUCT_MEMBER_STREAM_FEATURE.keys(): + return STRUCT_MEMBER_STREAM_FEATURE[key] + return None + + def shouldSkip(self) -> bool: + return ("api" in self.attribs.keys() + and not "vulkan" == self.attribs["api"]) + +# Is an S-expression w/ the following spec: +# From https://gist.github.com/pib/240957 +class Atom(object): + def __init__(self, name): + self.name = name + def __repr__(self,): + return self.name + +def parse_sexp(sexp): + atom_end = set('()"\'') | set(whitespace) + stack, i, length = [[]], 0, len(sexp) + while i < length: + c = sexp[i] + + reading = type(stack[-1]) + if reading == list: + if c == '(': stack.append([]) + elif c == ')': + stack[-2].append(stack.pop()) + if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) + elif c == '"': stack.append('') + elif c == "'": stack.append([('quote',)]) + elif c in whitespace: pass + else: stack.append(Atom(c)) + elif reading == str: + if c == '"': + stack[-2].append(stack.pop()) + if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) + elif c == '\\': + i += 1 + stack[-1] += sexp[i] + else: stack[-1] += c + elif reading == Atom: + if c in atom_end: + atom = stack.pop() + if atom.name[0].isdigit(): stack[-1].append(eval(atom.name)) + else: stack[-1].append(atom) + if stack[-1][0] == ('quote',): stack[-2].append(stack.pop()) + continue + else: stack[-1] = Atom(stack[-1].name + c) + i += 1 + + return stack.pop() + +class FuncExprVal(object): + def __init__(self, val): + self.val = val + def __repr__(self,): + return self.val.__repr__() + +class FuncExpr(object): + def __init__(self, name, args): + self.name = name + self.args = args + def __repr__(self,): + if len(self.args) == 0: + return "(%s)" % (self.name.__repr__()) + else: + return "(%s %s)" % (self.name.__repr__(), " ".join(map(lambda x: x.__repr__(), self.args))) + +class FuncLambda(object): + def __init__(self, vs, body): + self.vs = vs + self.body = body + def __repr__(self,): + return "(L (%s) %s)" % (" ".join(map(lambda x: x.__repr__(), self.vs)), self.body.__repr__()) + +class FuncLambdaParam(object): + def __init__(self, name, typ): + self.name = name + self.typ = typ + def __repr__(self,): + return "%s : %s" % (self.name, self.typ) + +def parse_func_expr(parsed_sexp): + if len(parsed_sexp) != 1: + print("Error: parsed # expressions != 1: %d" % (len(parsed_sexp))) + raise + + e = parsed_sexp[0] + + def parse_lambda_param(e): + return FuncLambdaParam(e[0].name, e[1].name) + + def parse_one(exp): + if list == type(exp): + if "lambda" == exp[0].__repr__(): + return FuncLambda(list(map(parse_lambda_param, exp[1])), parse_one(exp[2])) + else: + return FuncExpr(exp[0], list(map(parse_one, exp[1:]))) + else: + return FuncExprVal(exp) + + return parse_one(e) + +def parseFilterFuncExpr(expr): + res = parse_func_expr(parse_sexp(expr)) + print("parseFilterFuncExpr: parsed %s" % res) + return res + +def parseLetBodyExpr(expr): + res = parse_func_expr(parse_sexp(expr)) + print("parseLetBodyExpr: parsed %s" % res) + return res + + +def makeVulkanTypeFromXMLTag(typeInfo, parentName: str, tag: Element) -> VulkanType: + res = VulkanType() + + # Process the length expression + + if tag.attrib.get("len") is not None: + lengths = tag.attrib.get("len").split(",") + res.lenExpr = lengths[0] + + # Calculate static array expression + + nametag = tag.find("name") + enumtag = tag.find("enum") + + if enumtag is not None: + res.staticArrExpr = enumtag.text + elif nametag is not None: + res.rawStaticArrExpr = noneStr(nametag.tail) + + dimensions = res.rawStaticArrExpr.count('[') + if dimensions == 1: + res.staticArrExpr = res.rawStaticArrExpr[1:-1] + elif dimensions > 1: + arraySizes = res.rawStaticArrExpr[1:-1].split('][') + res.staticArrExpr = '(' + \ + '*'.join(f'({size})' for size in arraySizes) + ')' + + # Determine const + + beforeTypePart = noneStr(tag.text) + + if "const" in beforeTypePart: + res.isConst = True + + # Calculate type and pointer info + for elem in tag: + if elem.tag == "name": + res.paramName = elem.text + if elem.tag == "type": + duringTypePart = noneStr(elem.text) + afterTypePart = noneStr(elem.tail) + # Now we know enough to fill some stuff in + res.typeName = duringTypePart + + if res.typeName in TRANSFORMED_TYPES: + res.isTransformed = True + + # This only handles pointerIndirectionLevels == 2 + # along with optional constant pointer for the inner part. + for c in afterTypePart: + if c == "*": + res.pointerIndirectionLevels += 1 + if "const" in afterTypePart and res.pointerIndirectionLevels == 2: + res.isPointerToConstPointer = True + + # If void*, treat like it's not a pointer + # if duringTypePart == "void": + # res.pointerIndirectionLevels -= 1 + + # Calculate optionality (based on validitygenerator.py) + if tag.attrib.get("optional") is not None: + res.isOptional = True + res.optionalStr = tag.attrib.get("optional") + + # If no validity is being generated, it usually means that + # validity is complex and not absolute, so let's say yes. + if tag.attrib.get("noautovalidity") is not None: + res.isOptional = True + + # If this is a structure extension, it is optional. + if tag.attrib.get("structextends") is not None: + res.isOptional = True + + # If this is a pNext pointer, it is optional. + if res.paramName == "pNext": + res.isOptional = True + + res.primitiveEncodingSize = typeInfo.getPrimitiveEncodingSize(res.typeName) + + # Annotations: Environment binds + if tag.attrib.get("binds") is not None: + bindPairs = map(lambda x: x.strip(), tag.attrib.get("binds").split(",")) + bindPairsSplit = map(lambda p: p.split(":"), bindPairs) + res.binds = dict(map(lambda sp: (sp[0].strip(), sp[1].strip()), bindPairsSplit)) + + # Annotations: Filters + structMemberName = f"{parentName}.{res.paramName}" + if structMemberName in STRUCT_MEMBER_FILTER_VAR.keys(): + res.filterVar = STRUCT_MEMBER_FILTER_VAR[structMemberName] + + if structMemberName in STRUCT_MEMBER_FILTER_VALS.keys(): + res.filterVals = STRUCT_MEMBER_FILTER_VALS[structMemberName] + + if structMemberName in STRUCT_MEMBER_FILTER_FUNC.keys(): + res.filterFunc = parseFilterFuncExpr(STRUCT_MEMBER_FILTER_FUNC[structMemberName]) + + if tag.attrib.get("filterOtherwise") is not None: + res.Otherwise = tag.attrib.get("filterOtherwise") + + # store all other attribs here + res.attribs = dict(tag.attrib) + + return res + + +def makeVulkanTypeSimple(isConst, + typeName, + ptrIndirectionLevels, + paramName=None): + res = VulkanType() + + res.typeName = typeName + res.isConst = isConst + res.pointerIndirectionLevels = ptrIndirectionLevels + res.isPointerToConstPointer = False + res.paramName = paramName + res.primitiveEncodingSize = None + + return res + + +# Classes for describing aggregate types (unions, structs) and API calls. +class VulkanCompoundType(object): + + def __init__(self, name: str, members: List[VulkanType], isUnion=False, structEnumExpr=None, structExtendsExpr=None, feature=None, initialEnv={}, optional=None): + self.name: str = name + self.typeName: str = name + self.members: List[VulkanType] = members + self.environment = initialEnv + self.isUnion = isUnion + self.structEnumExpr = structEnumExpr + self.structExtendsExpr = structExtendsExpr + self.feature = feature + if name in DEVICE_MEMORY_STRUCTS: + self.deviceMemoryInfoParameterIndices = DEVICE_MEMORY_STRUCTS[name] + else: + self.deviceMemoryInfoParameterIndices = None + self.isTransformed = name in TRANSFORMED_TYPES + self.copy = None + self.optionalStr = optional + + def initCopies(self): + self.copy = self + + for m in self.members: + m.parent = self.copy + + def getMember(self, memberName) -> Optional[VulkanType]: + for m in self.members: + if m.paramName == memberName: + return m + return None + + def getStructEnumExpr(self,): + return self.structEnumExpr + + def getProtectStreamFeature(self) -> Optional[str]: + if not self.name in STRUCT_STREAM_FEATURE.keys(): + return None + return STRUCT_STREAM_FEATURE[self.name] + + +class VulkanAPI(object): + + def __init__(self, name: str, retType: VulkanType, parameters, origName=None): + self.name: str = name + self.origName = name + self.retType: VulkanType = retType + self.parameters: List[VulkanType] = list(filter(lambda param: not param.shouldSkip(), parameters)) + + if name in DEVICE_MEMORY_COMMANDS.keys(): + self.deviceMemoryInfoParameterIndices = DEVICE_MEMORY_COMMANDS[name] + else: + self.deviceMemoryInfoParameterIndices = None + + self.copy = None + + self.isTransformed = name in TRANSFORMED_TYPES + + if origName: + self.origName = origName + + def initCopies(self): + self.copy = self + + for m in self.parameters: + m.parent = self.copy + + def getCopy(self,): + return copy(self) + + def getParameter(self, parameterName): + for p in self.parameters: + if p.paramName == parameterName: + return p + return None + + def withModifiedName(self, newName): + res = VulkanAPI(newName, self.retType, self.parameters) + return res + + def getRetVarExpr(self): + if self.retType.typeName == "void": + return None + return "%s_%s_return" % (self.name, self.retType.typeName) + + def getRetTypeExpr(self): + return self.retType.typeName + + def withCustomParameters(self, customParams): + res = self.getCopy() + res.parameters = customParams + return res + + def withCustomReturnType(self, retType): + res = self.getCopy() + res.retType = retType + return res + +# Whether or not special handling of virtual elements +# such as VkDeviceMemory is needed. +def vulkanTypeNeedsTransform(structOrApi): + return structOrApi.deviceMemoryInfoParameterIndices != None + +def vulkanTypeGetNeededTransformTypes(structOrApi): + res = [] + if structOrApi.deviceMemoryInfoParameterIndices != None: + res.append("devicememory") + return res + +def vulkanTypeforEachSubType(structOrApi, f): + toLoop = None + if type(structOrApi) == VulkanCompoundType: + toLoop = structOrApi.members + if type(structOrApi) == VulkanAPI: + toLoop = structOrApi.parameters + + for (i, x) in enumerate(toLoop): + f(i, x) + +# Parses everything about Vulkan types into a Python readable format. +class VulkanTypeInfo(object): + + def __init__(self, generator): + self.generator = generator + self.categories: Set[str] = set([]) + + # Tracks what Vulkan type is part of what category. + self.typeCategories: Dict[str, str] = {} + + # Tracks the primitive encoding size for each type, if applicable. + self.encodingSizes: Dict[str, Optional[int]] = {} + + self.structs: Dict[str, VulkanCompoundType] = {} + self.apis: Dict[str, VulkanAPI] = {} + + # Maps bitmask types to the enum type used for the flags + # E.g. "VkImageAspectFlags" -> "VkImageAspectFlagBits" + self.bitmasks: Dict[str, str] = {} + + # Maps all enum names to their values. + # For aliases, the value is the name of the canonical enum + self.enumValues: Dict[str, Union[int, str]] = {} + + # Maps enum to their xml element + self.enumElem = {} + + self.feature = None + + def initType(self, name: str, category: str): + self.categories.add(category) + self.typeCategories[name] = category + self.encodingSizes[name] = self.setPrimitiveEncodingSize(name) + + def categoryOf(self, name): + return self.typeCategories[name] + + def getPrimitiveEncodingSize(self, name): + return self.encodingSizes[name] + + # Queries relating to categories of Vulkan types. + def isHandleType(self, name): + return self.typeCategories.get(name) == "handle" + + def isCompoundType(self, name: str): + return self.typeCategories.get(name) in ["struct", "union"] + + # Gets the best size in bytes + # for encoding/decoding a particular Vulkan type. + # If not applicable, returns None. + def setPrimitiveEncodingSize(self, name: str) -> Optional[int]: + baseEncodingSizes = { + "void": 8, + "char": 1, + "float": 4, + "uint8_t": 1, + "uint16_t": 2, + "uint32_t": 4, + "uint64_t": 8, + "int": 4, + "int8_t": 1, + "int16_t": 2, + "int32_t": 4, + "int64_t": 8, + "size_t": 8, + "ssize_t": 8, + "VkBool32": 4, + "zx_handle_t": 4, + } + + if name in baseEncodingSizes: + return baseEncodingSizes[name] + + category = self.typeCategories[name] + + if category in [None, "api", "include", "define", "struct", "union"]: + return None + + # Handles are pointers so they must be 8 bytes. Basetype includes VkDeviceSize which is 8 bytes. + if category in ["handle", "basetype", "funcpointer"]: + return 8 + + if category in ["enum", "bitmask"]: + return 4 + + def isNonAbiPortableType(self, typeName): + if typeName in EXPLICITLY_ABI_PORTABLE_TYPES: + return False + + if typeName in EXPLICITLY_ABI_NON_PORTABLE_TYPES: + return True + + category = self.typeCategories[typeName] + return category in NON_ABI_PORTABLE_TYPE_CATEGORIES + + def onBeginFeature(self, featureName, featureType): + self.feature = featureName + + def onEndFeature(self): + self.feature = None + + def onGenType(self, typeinfo, name, alias): + category = typeinfo.elem.get("category") + self.initType(name, category) + + if category in ["struct", "union"]: + self.onGenStruct(typeinfo, name, alias) + + if category == "bitmask": + self.bitmasks[name] = typeinfo.elem.get("requires") + + def onGenStruct(self, typeinfo, typeName, alias): + if not alias: + members: List[VulkanType] = [] + + structExtendsExpr = typeinfo.elem.get("structextends") + + structEnumExpr = None + + initialEnv = {} + envStr = typeinfo.elem.get("exists") + if envStr != None: + comma_separated = envStr.split(",") + name_type_pairs = map(lambda cs: tuple(map(lambda t: t.strip(), cs.split(":"))), comma_separated) + for (name, typ) in name_type_pairs: + initialEnv[name] = { + "type" : typ, + "binding" : None, + "structmember" : False, + "body" : None, + } + + if typeName in STRUCT_ENV_STR.keys(): + name_body_pairs = STRUCT_ENV_STR[typeName] + for (name, body) in name_body_pairs.items(): + initialEnv[name] = { + "type" : "uint32_t", + "binding" : name, + "structmember" : False, + "body" : parseLetBodyExpr(body) + } + + for member in typeinfo.elem.findall(".//member"): + if "api" in member.attrib.keys() and not "vulkan" == member.attrib["api"]: + continue + vulkanType = makeVulkanTypeFromXMLTag(self, typeName, member) + initialEnv[vulkanType.paramName] = { + "type": vulkanType.typeName, + "binding": vulkanType.paramName, + "structmember": True, + "body": None, + } + members.append(vulkanType) + if vulkanType.typeName == "VkStructureType" and \ + member.get("values"): + structEnumExpr = member.get("values") + + self.structs[typeName] = \ + VulkanCompoundType( + typeName, + members, + isUnion = self.categoryOf(typeName) == "union", + structEnumExpr = structEnumExpr, + structExtendsExpr = structExtendsExpr, + feature = self.feature, + initialEnv = initialEnv, + optional = typeinfo.elem.get("optional", None)) + self.structs[typeName].initCopies() + + def onGenGroup(self, groupinfo, groupName, _alias=None): + self.initType(groupName, "enum") + enums = groupinfo.elem.findall("enum") + for enum in enums: + intVal, strVal = self.generator.enumToValue(enum, True) + self.enumValues[enum.get('name')] = intVal if intVal is not None else strVal + self.enumElem[enum.get('name')] = enum + + + def onGenEnum(self, enuminfo, name: str, alias): + self.initType(name, "enum") + value: str = enuminfo.elem.get("value") + self.enumElem[name] = enuminfo.elem + if value and value.isdigit(): + self.enumValues[name] = int(value) + elif value and value[0] == '"' and value[-1] == '"': + self.enumValues[name] = value[1:-1] + elif alias is not None: + self.enumValues[name] = alias + else: + # There's about a dozen cases of using the bitwise NOT operator (e.g.: `(~0U)`, `(~0ULL)`) + # to concisely represent large values. Just ignore them for now. + # In the future, we can add a lookup table to convert these to int + return + + def onGenCmd(self, cmdinfo, name, _alias): + self.initType(name, "api") + + proto = cmdinfo.elem.find("proto") + params = cmdinfo.elem.findall("param") + + self.apis[name] = \ + VulkanAPI( + name, + makeVulkanTypeFromXMLTag(self, name, proto), + list(map(lambda p: makeVulkanTypeFromXMLTag(self, name, p), + params))) + self.apis[name].initCopies() + + def onEnd(self): + pass + +def hasNullOptionalStringFeature(forEachType): + return (hasattr(forEachType, "onCheckWithNullOptionalStringFeature")) and \ + (hasattr(forEachType, "endCheckWithNullOptionalStringFeature")) and \ + (hasattr(forEachType, "finalCheckWithNullOptionalStringFeature")) + + +# General function to iterate over a vulkan type and call code that processes +# each of its sub-components, if any. +def iterateVulkanType(typeInfo: VulkanTypeInfo, vulkanType: VulkanType, forEachType): + if not vulkanType.isArrayOfStrings(): + if vulkanType.isPointerToConstPointer: + return False + + if vulkanType.shouldSkip(): + return False + + forEachType.registerTypeInfo(typeInfo) + + needCheck = vulkanType.isOptionalPointer() + + if typeInfo.isCompoundType(vulkanType.typeName) and not vulkanType.isNextPointer(): + + if needCheck: + forEachType.onCheck(vulkanType) + + forEachType.onCompoundType(vulkanType) + + if needCheck: + forEachType.endCheck(vulkanType) + + else: + if vulkanType.isString(): + if needCheck and hasNullOptionalStringFeature(forEachType): + forEachType.onCheckWithNullOptionalStringFeature(vulkanType) + forEachType.onString(vulkanType) + forEachType.endCheckWithNullOptionalStringFeature(vulkanType) + forEachType.onString(vulkanType) + forEachType.finalCheckWithNullOptionalStringFeature(vulkanType) + elif needCheck: + forEachType.onCheck(vulkanType) + forEachType.onString(vulkanType) + forEachType.endCheck(vulkanType) + else: + forEachType.onString(vulkanType) + + elif vulkanType.isArrayOfStrings(): + forEachType.onStringArray(vulkanType) + + elif vulkanType.staticArrExpr: + forEachType.onStaticArr(vulkanType) + + elif vulkanType.isNextPointer(): + if needCheck: + forEachType.onCheck(vulkanType) + forEachType.onStructExtension(vulkanType) + if needCheck: + forEachType.endCheck(vulkanType) + + elif vulkanType.pointerIndirectionLevels > 0: + if needCheck: + forEachType.onCheck(vulkanType) + forEachType.onPointer(vulkanType) + if needCheck: + forEachType.endCheck(vulkanType) + + else: + forEachType.onValue(vulkanType) + + return True + +class VulkanTypeIterator(object): + def __init__(self,): + self.typeInfo = None + + def registerTypeInfo(self, typeInfo): + self.typeInfo = typeInfo + +def vulkanTypeGetStructFieldLengthInfo(structInfo, vulkanType): + def getSpecialCaseVulkanStructFieldLength(structInfo, vulkanType): + cases = [ + { + "structName": "VkShaderModuleCreateInfo", + "field": "pCode", + "lenExpr": "codeSize", + "postprocess": lambda expr: "(%s / 4)" % expr + }, + { + "structName": "VkPipelineMultisampleStateCreateInfo", + "field": "pSampleMask", + "lenExpr": "rasterizationSamples", + "postprocess": lambda expr: "(((%s) + 31) / 32)" % expr + }, + ] + + for c in cases: + if (structInfo.name, vulkanType.paramName) == (c["structName"], c["field"]): + return c + + return None + + specialCaseAccess = getSpecialCaseVulkanStructFieldLength(structInfo, vulkanType) + + if specialCaseAccess is not None: + return specialCaseAccess + + lenExpr = vulkanType.getLengthExpression() + + if lenExpr is None: + return None + + return { + "structName": structInfo.name, + "field": vulkanType.typeName, + "lenExpr": lenExpr, + "postprocess": lambda expr: expr} + + +class VulkanTypeProtobufInfo(object): + def __init__(self, typeInfo, structInfo, vulkanType): + self.needsMessage = typeInfo.isCompoundType(vulkanType.typeName) + self.isRepeatedString = vulkanType.isArrayOfStrings() + self.isString = vulkanType.isString() or ( + vulkanType.typeName == "char" and (vulkanType.staticArrExpr != "")) + + if structInfo is not None: + self.lengthInfo = vulkanTypeGetStructFieldLengthInfo( + structInfo, vulkanType) + else: + self.lengthInfo = vulkanType.getLengthExpression() + + self.protobufType = None + self.origTypeCategory = typeInfo.categoryOf(vulkanType.typeName) + + self.isExtensionStruct = \ + vulkanType.typeName == "void" and \ + vulkanType.pointerIndirectionLevels > 0 and \ + vulkanType.paramName == "pNext" + + if self.needsMessage: + return + + if typeInfo.categoryOf(vulkanType.typeName) in ["enum", "bitmask"]: + self.protobufType = "uint32" + + if typeInfo.categoryOf(vulkanType.typeName) in ["funcpointer", "handle", "define"]: + self.protobufType = "uint64" + + if typeInfo.categoryOf(vulkanType.typeName) in ["basetype"]: + baseTypeMapping = { + "VkFlags" : "uint32", + "VkBool32" : "uint32", + "VkDeviceSize" : "uint64", + "VkSampleMask" : "uint32", + } + self.protobufType = baseTypeMapping[vulkanType.typeName] + + if typeInfo.categoryOf(vulkanType.typeName) == None: + + otherTypeMapping = { + "void" : "uint64", + "char" : "uint8", + "size_t" : "uint64", + "float" : "float", + "uint8_t" : "uint32", + "uint16_t" : "uint32", + "int32_t" : "int32", + "uint32_t" : "uint32", + "uint64_t" : "uint64", + "VkDeviceSize" : "uint64", + "VkSampleMask" : "uint32", + } + + if vulkanType.typeName in otherTypeMapping: + self.protobufType = otherTypeMapping[vulkanType.typeName] + else: + self.protobufType = "uint64" + + + protobufCTypeMapping = { + "uint8" : "uint8_t", + "uint32" : "uint32_t", + "int32" : "int32_t", + "uint64" : "uint64_t", + "float" : "float", + "string" : "const char*", + } + + self.protobufCType = protobufCTypeMapping[self.protobufType] + diff --git a/src/gfxstream/codegen/scripts/cereal/counting.py b/src/gfxstream/codegen/scripts/cereal/counting.py new file mode 100644 index 00000000000..849ceafc3e4 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/counting.py @@ -0,0 +1,696 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import copy + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator, Atom, FuncExpr, FuncExprVal, FuncLambda + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import ROOT_TYPE_VAR_NAME, ROOT_TYPE_PARAM +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME + +class VulkanCountingCodegen(VulkanTypeIterator): + def __init__(self, cgen, featureBitsVar, toCountVar, countVar, rootTypeVar, prefix, forApiOutput=False, mapHandles=True, handleMapOverwrites=False, doFiltering=True): + self.cgen = cgen + self.featureBitsVar = featureBitsVar + self.toCountVar = toCountVar + self.rootTypeVar = rootTypeVar + self.countVar = countVar + self.prefix = prefix + self.forApiOutput = forApiOutput + + self.exprAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = True) + self.exprValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = False) + self.exprPrimitiveValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = False) + + self.lenAccessor = lambda t: self.cgen.generalLengthAccess(t, parentVarName = self.toCountVar) + self.lenAccessorGuard = lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName = self.toCountVar) + self.filterVarAccessor = lambda t: self.cgen.filterVarAccess(t, parentVarName = self.toCountVar) + + self.checked = False + + self.mapHandles = mapHandles + self.handleMapOverwrites = handleMapOverwrites + self.doFiltering = doFiltering + + def getTypeForStreaming(self, vulkanType): + res = copy(vulkanType) + + if not vulkanType.accessibleAsPointer(): + res = res.getForAddressAccess() + + if vulkanType.staticArrExpr: + res = res.getForAddressAccess() + + return res + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def genCount(self, sizeExpr): + self.cgen.stmt("*%s += %s" % (self.countVar, sizeExpr)) + + def genPrimitiveStreamCall(self, vulkanType): + self.genCount(str(self.cgen.countPrimitive( + self.typeInfo, + vulkanType))) + + def genHandleMappingCall(self, vulkanType, access, lenAccess): + + if lenAccess is None: + lenAccess = "1" + handle64Bytes = "8" + else: + handle64Bytes = "%s * 8" % lenAccess + + handle64Var = self.cgen.var() + if lenAccess != "1": + self.cgen.beginIf(lenAccess) + # self.cgen.stmt("uint64_t* %s" % handle64Var) + # self.cgen.stmt( + # "%s->alloc((void**)&%s, %s * 8)" % \ + # (self.streamVarName, handle64Var, lenAccess)) + handle64VarAccess = handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 1, paramName=handle64Var) + else: + self.cgen.stmt("uint64_t %s" % handle64Var) + handle64VarAccess = "&%s" % handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 0, paramName=handle64Var) + + if self.handleMapOverwrites: + # self.cgen.stmt( + # "static_assert(8 == sizeof(%s), \"handle map overwrite requres %s to be 8 bytes long\")" % \ + # (vulkanType.typeName, vulkanType.typeName)) + # self.cgen.stmt( + # "%s->handleMapping()->mapHandles_%s((%s*)%s, %s)" % + # (self.streamVarName, vulkanType.typeName, vulkanType.typeName, + # access, lenAccess)) + self.genCount("8 * %s" % lenAccess) + else: + # self.cgen.stmt( + # "%s->handleMapping()->mapHandles_%s_u64(%s, %s, %s)" % + # (self.streamVarName, vulkanType.typeName, + # access, + # handle64VarAccess, lenAccess)) + self.genCount(handle64Bytes) + + if lenAccess != "1": + self.cgen.endIf() + + def doAllocSpace(self, vulkanType): + pass + + def getOptionalStringFeatureExpr(self, vulkanType): + feature = vulkanType.getProtectStreamFeature() + if feature is None: + return None + return "%s & %s" % (self.featureBitsVar, feature) + + def onCheck(self, vulkanType): + + if self.forApiOutput: + return + + featureExpr = self.getOptionalStringFeatureExpr(vulkanType); + + self.checked = True + + access = self.exprAccessor(vulkanType) + + needConsistencyCheck = False + + self.cgen.line("// WARNING PTR CHECK") + checkAccess = self.exprAccessor(vulkanType) + addrExpr = "&" + checkAccess + sizeExpr = self.cgen.sizeofExpr(vulkanType) + + if featureExpr is not None: + self.cgen.beginIf(featureExpr) + + self.genPrimitiveStreamCall( + vulkanType) + + if featureExpr is not None: + self.cgen.endIf() + + if featureExpr is not None: + self.cgen.beginIf("(!(%s) || %s)" % (featureExpr, access)) + else: + self.cgen.beginIf(access) + + if needConsistencyCheck and featureExpr is None: + self.cgen.beginIf("!(%s)" % checkName) + self.cgen.stmt( + "fprintf(stderr, \"fatal: %s inconsistent between guest and host\\n\")" % (access)) + self.cgen.endIf() + + + def onCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.beginIf("%s & VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT" % self.featureBitsVar) + self.onCheck(vulkanType) + + def endCheckWithNullOptionalStringFeature(self, vulkanType): + self.endCheck(vulkanType) + self.cgen.endIf() + self.cgen.beginElse() + + def finalCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.endElse() + + def endCheck(self, vulkanType): + + if self.checked: + self.cgen.endIf() + self.checked = False + + def genFilterFunc(self, filterfunc, env): + + def loop(expr, lambdaEnv={}): + def do_func(expr): + fnamestr = expr.name.name + if "not" == fnamestr: + return "!(%s)" % (loop(expr.args[0], lambdaEnv)) + if "eq" == fnamestr: + return "(%s == %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "and" == fnamestr: + return "(%s && %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "or" == fnamestr: + return "(%s || %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "bitwise_and" == fnamestr: + return "(%s & %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "getfield" == fnamestr: + ptrlevels = get_ptrlevels(expr.args[0].val.name) + if ptrlevels == 0: + return "%s.%s" % (loop(expr.args[0], lambdaEnv), expr.args[1].val) + else: + return "(%s(%s)).%s" % ("*" * ptrlevels, loop(expr.args[0], lambdaEnv), expr.args[1].val) + + if "if" == fnamestr: + return "((%s) ? (%s) : (%s))" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv), loop(expr.args[2], lambdaEnv)) + + return "%s(%s)" % (fnamestr, ", ".join(map(lambda e: loop(e, lambdaEnv), expr.args))) + + def do_expratom(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return atomname + + enventry = env.get(atomname, None) + if None != enventry: + return self.getEnvAccessExpr(atomname) + return atomname + + def get_ptrlevels(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return 0 + + enventry = env.get(atomname, None) + if None != enventry: + return self.getPointerIndirectionLevels(atomname) + + return 0 + + def do_exprval(expr, lambdaEnv= {}): + expratom = expr.val + + if Atom == type(expratom): + return do_expratom(expratom.name, lambdaEnv) + + return "%s" % expratom + + def do_lambda(expr, lambdaEnv= {}): + params = expr.vs + body = expr.body + newEnv = {} + + for (k, v) in lambdaEnv.items(): + newEnv[k] = v + + for p in params: + newEnv[p.name] = p.typ + + return "[](%s) { return %s; }" % (", ".join(list(map(lambda p: "%s %s" % (p.typ, p.name), params))), loop(body, lambdaEnv=newEnv)) + + if FuncExpr == type(expr): + return do_func(expr) + if FuncLambda == type(expr): + return do_lambda(expr) + elif FuncExprVal == type(expr): + return do_exprval(expr) + + return loop(filterfunc) + + def beginFilterGuard(self, vulkanType): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + filterVarAccess = self.getEnvAccessExpr(vulkanType.filterVar) + + filterValsExpr = None + filterFuncExpr = None + filterExpr = None + + filterFeature = "%s & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.featureBitsVar + + if None != vulkanType.filterVals: + filterValsExpr = " || ".join(map(lambda filterval: "(%s == %s)" % (filterval, filterVarAccess), vulkanType.filterVals)) + + if None != vulkanType.filterFunc: + filterFuncExpr = self.genFilterFunc(vulkanType.filterFunc, self.currentStructInfo.environment) + + if None != filterValsExpr and None != filterFuncExpr: + filterExpr = "%s || %s" % (filterValsExpr, filterFuncExpr) + elif None == filterValsExpr and None == filterFuncExpr: + # Assume is bool + self.cgen.beginIf(filterVarAccess) + elif None != filterValsExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterValsExpr)) + elif None != filterFuncExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterFuncExpr)) + + def endFilterGuard(self, vulkanType, cleanupExpr=None): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + if cleanupExpr == None: + self.cgen.endIf() + else: + self.cgen.endIf() + self.cgen.beginElse() + self.cgen.stmt(cleanupExpr) + self.cgen.endElse() + + def getEnvAccessExpr(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + envAccess = self.exprValueAccessor(list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0]) + else: + envAccess = varName + return envAccess + + return None + + def getPointerIndirectionLevels(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + return list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0].pointerIndirectionLevels + else: + return 0 + return 0 + + return 0 + + + def onCompoundType(self, vulkanType): + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.beginFilterGuard(vulkanType) + + if vulkanType.pointerIndirectionLevels > 0: + self.doAllocSpace(vulkanType) + + if lenAccess is not None: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + loopVar = "i" + access = "%s + %s" % (access, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess) + forIncr = "++%s" % loopVar + self.cgen.beginFor(forInit, forCond, forIncr) + + accessWithCast = "%s(%s)" % (self.makeCastExpr( + self.getTypeForStreaming(vulkanType)), access) + + callParams = [self.featureBitsVar, + self.rootTypeVar, accessWithCast, self.countVar] + + for (bindName, localName) in vulkanType.binds.items(): + callParams.append(self.getEnvAccessExpr(localName)) + + self.cgen.funcCall(None, self.prefix + vulkanType.typeName, + callParams) + + if lenAccess is not None: + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + + self.endFilterGuard(vulkanType) + + def onString(self, vulkanType): + access = self.exprAccessor(vulkanType) + self.genCount("sizeof(uint32_t) + (%s ? strlen(%s) : 0)" % (access, access)) + + def onStringArray(self, vulkanType): + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.genCount("sizeof(uint32_t)") + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i") + self.cgen.stmt("size_t l = %s[i] ? strlen(%s[i]) : 0" % (access, access)) + self.genCount("sizeof(uint32_t) + (%s[i] ? strlen(%s[i]) : 0)" % (access, access)) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + + def onStaticArr(self, vulkanType): + access = self.exprValueAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + finalLenExpr = "%s * %s" % (lenAccess, self.cgen.sizeofExpr(vulkanType)) + if lenAccessGuard is not None: + self.cgen.endIf() + self.genCount(finalLenExpr) + + def onStructExtension(self, vulkanType): + sTypeParam = copy(vulkanType) + sTypeParam.paramName = "sType" + + access = self.exprAccessor(vulkanType) + sizeVar = "%s_size" % vulkanType.paramName + + castedAccessExpr = access + + sTypeAccess = self.exprAccessor(sTypeParam) + self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" % + self.rootTypeVar) + self.cgen.stmt("%s = %s" % (self.rootTypeVar, sTypeAccess)) + self.cgen.endIf() + + self.cgen.funcCall(None, self.prefix + "extension_struct", + [self.featureBitsVar, self.rootTypeVar, castedAccessExpr, self.countVar]) + + + def onPointer(self, vulkanType): + access = self.exprAccessor(vulkanType) + + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.beginFilterGuard(vulkanType) + self.doAllocSpace(vulkanType) + + if vulkanType.filterVar != None: + print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + + if vulkanType.isHandleType() and self.mapHandles: + self.genHandleMappingCall(vulkanType, access, lenAccess) + else: + if self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + if lenAccess is not None: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t i = 0", "i < (uint32_t)%s" % lenAccess, "++i") + self.genPrimitiveStreamCall(vulkanType.getForValueAccess()) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + else: + self.genPrimitiveStreamCall(vulkanType.getForValueAccess()) + else: + if lenAccess is not None: + needLenAccessGuard = True + finalLenExpr = "%s * %s" % ( + lenAccess, self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + else: + needLenAccessGuard = False + finalLenExpr = "%s" % ( + self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + if needLenAccessGuard and lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.genCount(finalLenExpr) + if needLenAccessGuard and lenAccessGuard is not None: + self.cgen.endIf() + + self.endFilterGuard(vulkanType) + + def onValue(self, vulkanType): + self.beginFilterGuard(vulkanType) + + if vulkanType.isHandleType() and self.mapHandles: + access = self.exprAccessor(vulkanType) + if vulkanType.filterVar != None: + print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + self.genHandleMappingCall( + vulkanType.getForAddressAccess(), access, "1") + elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + access = self.exprPrimitiveValueAccessor(vulkanType) + self.genPrimitiveStreamCall(vulkanType) + else: + access = self.exprAccessor(vulkanType) + self.genCount(self.cgen.sizeofExpr(vulkanType)) + + self.endFilterGuard(vulkanType) + + def streamLetParameter(self, structInfo, letParamInfo): + filterFeature = "%s & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % (self.featureBitsVar) + self.cgen.stmt("%s %s = 1" % (letParamInfo.typeName, letParamInfo.paramName)) + + self.cgen.beginIf(filterFeature) + + bodyExpr = self.currentStructInfo.environment[letParamInfo.paramName]["body"] + self.cgen.stmt("%s = %s" % (letParamInfo.paramName, self.genFilterFunc(bodyExpr, self.currentStructInfo.environment))) + + self.genPrimitiveStreamCall(letParamInfo) + + self.cgen.endIf() + +class VulkanCounting(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.featureBitsVar = "featureBits" + self.featureBitsVarType = makeVulkanTypeSimple(False, "uint32_t", 0, self.featureBitsVar) + self.countingPrefix = "count_" + self.countVars = ["toCount", "count"] + self.countVarType = makeVulkanTypeSimple(False, "size_t", 1, self.countVars[1]) + self.voidType = makeVulkanTypeSimple(False, "void", 0) + self.rootTypeVar = ROOT_TYPE_VAR_NAME + + self.countingCodegen = \ + VulkanCountingCodegen( + self.codegen, + self.featureBitsVar, + self.countVars[0], + self.countVars[1], + self.rootTypeVar, + self.countingPrefix) + + self.knownDefs = {} + + self.extensionCountingPrototype = \ + VulkanAPI(self.countingPrefix + "extension_struct", + self.voidType, + [self.featureBitsVarType, + ROOT_TYPE_PARAM, + STRUCT_EXTENSION_PARAM, + self.countVarType]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.codegen.makeFuncDecl( + self.extensionCountingPrototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + # TODO(liyl): might not work if freeParams != [] + self.module.appendHeader( + self.codegen.makeFuncAlias(self.countingPrefix + name, + self.countingPrefix + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + freeParams = [] + letParams = [] + + for (envname, bindingInfo) in list(sorted(structInfo.environment.items(), key = lambda kv: kv[0])): + if None == bindingInfo["binding"]: + freeParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + else: + if not bindingInfo["structmember"]: + letParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + + typeFromName = \ + lambda varname: \ + makeVulkanTypeSimple(True, name, 1, varname) + + countingParams = \ + [makeVulkanTypeSimple(False, "uint32_t", 0, self.featureBitsVar), + ROOT_TYPE_PARAM, + typeFromName(self.countVars[0]), + makeVulkanTypeSimple(False, "size_t", 1, self.countVars[1])] + + countingPrototype = \ + VulkanAPI(self.countingPrefix + name, + self.voidType, + countingParams + freeParams) + + countingPrototypeNoFilter = \ + VulkanAPI(self.countingPrefix + name, + self.voidType, + countingParams) + + def structCountingDef(cgen): + self.countingCodegen.cgen = cgen + self.countingCodegen.currentStructInfo = structInfo + cgen.stmt("(void)%s" % self.featureBitsVar); + cgen.stmt("(void)%s" % self.rootTypeVar); + cgen.stmt("(void)%s" % self.countVars[0]); + cgen.stmt("(void)%s" % self.countVars[1]); + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.countingCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.countingCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.countingCodegen) + + def structCountingDefNoFilter(cgen): + self.countingCodegen.cgen = cgen + self.countingCodegen.currentStructInfo = structInfo + self.countingCodegen.doFiltering = False + cgen.stmt("(void)%s" % self.featureBitsVar); + cgen.stmt("(void)%s" % self.rootTypeVar); + cgen.stmt("(void)%s" % self.countVars[0]); + cgen.stmt("(void)%s" % self.countVars[1]); + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.countingCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.countingCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.countingCodegen) + + self.countingCodegen.doFiltering = True + + self.module.appendHeader( + self.codegen.makeFuncDecl(countingPrototype)) + self.module.appendImpl( + self.codegen.makeFuncImpl(countingPrototype, structCountingDef)) + + if freeParams != []: + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(countingPrototypeNoFilter)) + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + countingPrototypeNoFilter, structCountingDefNoFilter)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def doExtensionStructCountCodegen(self, cgen, extParam, forEach, funcproto): + accessVar = "structAccess" + sizeVar = "currExtSize" + cgen.stmt("VkInstanceCreateInfo* %s = (VkInstanceCreateInfo*)(%s)" % (accessVar, extParam.paramName)) + cgen.stmt("size_t %s = %s(%s, %s, %s)" % (sizeVar, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, + self.featureBitsVar, ROOT_TYPE_VAR_NAME, extParam.paramName)) + + cgen.beginIf("!%s && %s" % (sizeVar, extParam.paramName)) + + cgen.line("// unknown struct extension; skip and call on its pNext field"); + cgen.funcCall(None, funcproto.name, [ + self.featureBitsVar, ROOT_TYPE_VAR_NAME, "(void*)%s->pNext" % accessVar, self.countVars[1]]) + cgen.stmt("return") + + cgen.endIf() + cgen.beginElse() + + cgen.line("// known or null extension struct") + + cgen.stmt("*%s += sizeof(uint32_t)" % self.countVars[1]) + + cgen.beginIf("!%s" % (sizeVar)) + cgen.line("// exit if this was a null extension struct (size == 0 in this branch)") + cgen.stmt("return") + cgen.endIf() + + cgen.endIf() + + cgen.stmt("*%s += sizeof(VkStructureType)" % self.countVars[1]) + + def fatalDefault(cgen): + cgen.line("// fatal; the switch is only taken if the extension struct is known"); + cgen.stmt("abort()") + pass + + self.emitForEachStructExtension( + cgen, + makeVulkanTypeSimple(False, "void", 0, "void"), + extParam, + forEach, + defaultEmit=fatalDefault, + rootTypeVar=ROOT_TYPE_PARAM) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def forEachExtensionCounting(ext, castedAccess, cgen): + cgen.funcCall(None, self.countingPrefix + ext.name, + [self.featureBitsVar, self.rootTypeVar, castedAccess, self.countVars[1]]) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionCountingPrototype, + lambda cgen: self.doExtensionStructCountCodegen( + cgen, + STRUCT_EXTENSION_PARAM, + forEachExtensionCounting, + self.extensionCountingPrototype))) diff --git a/src/gfxstream/codegen/scripts/cereal/decoder.py b/src/gfxstream/codegen/scripts/cereal/decoder.py new file mode 100644 index 00000000000..270c767c6a1 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/decoder.py @@ -0,0 +1,946 @@ +from .common.codegen import CodeGen, VulkanWrapperGenerator +from .common.vulkantypes import VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeInfo,\ + VulkanType + +from .marshaling import VulkanMarshalingCodegen +from .reservedmarshaling import VulkanReservedMarshalingCodegen +from .transform import TransformCodegen + +from .wrapperdefs import API_PREFIX_MARSHAL +from .wrapperdefs import API_PREFIX_RESERVEDUNMARSHAL +from .wrapperdefs import MAX_PACKET_LENGTH +from .wrapperdefs import VULKAN_STREAM_TYPE +from .wrapperdefs import ROOT_TYPE_DEFAULT_VALUE +from .wrapperdefs import RELAXED_APIS + + +SKIPPED_DECODER_DELETES = [ + "vkFreeDescriptorSets", +] + +DELAYED_DECODER_DELETES = [ + "vkDestroyPipelineLayout", +] + +global_state_prefix = "m_state->on_" + +decoder_decl_preamble = """ + +namespace gfxstream { +class IOStream; +class ProcessResources; +} // namespace gfxstream + +namespace gfxstream { +namespace vk { + +class VkDecoder { +public: + VkDecoder(); + ~VkDecoder(); + void setForSnapshotLoad(bool forSnapshotLoad); + size_t decode(void* buf, size_t bufsize, IOStream* stream, + const ProcessResources* processResources, const VkDecoderContext&); +private: + class Impl; + std::unique_ptr mImpl; +}; + +} // namespace vk +} // namespace gfxstream + +""" + +decoder_impl_preamble =""" +namespace gfxstream { +namespace vk { + +using android::base::MetricEventBadPacketLength; +using android::base::MetricEventDuplicateSequenceNum; + +class VkDecoder::Impl { +public: + Impl() : m_logCalls(android::base::getEnvironmentVariable("ANDROID_EMU_VK_LOG_CALLS") == "1"), + m_vk(vkDispatch()), + m_state(VkDecoderGlobalState::get()), + m_boxedHandleUnwrapMapping(m_state), + m_boxedHandleCreateMapping(m_state), + m_boxedHandleDestroyMapping(m_state), + m_boxedHandleUnwrapAndDeleteMapping(m_state), + m_boxedHandleUnwrapAndDeletePreserveBoxedMapping(m_state), + m_prevSeqno(std::nullopt) {} + %s* stream() { return &m_vkStream; } + VulkanMemReadingStream* readStream() { return &m_vkMemReadingStream; } + + void setForSnapshotLoad(bool forSnapshotLoad) { + m_forSnapshotLoad = forSnapshotLoad; + } + + size_t decode(void* buf, size_t bufsize, IOStream* stream, + const ProcessResources* processResources, const VkDecoderContext&); + +private: + bool m_logCalls; + bool m_forSnapshotLoad = false; + VulkanDispatch* m_vk; + VkDecoderGlobalState* m_state; + %s m_vkStream { nullptr }; + VulkanMemReadingStream m_vkMemReadingStream { nullptr }; + BoxedHandleUnwrapMapping m_boxedHandleUnwrapMapping; + BoxedHandleCreateMapping m_boxedHandleCreateMapping; + BoxedHandleDestroyMapping m_boxedHandleDestroyMapping; + BoxedHandleUnwrapAndDeleteMapping m_boxedHandleUnwrapAndDeleteMapping; + android::base::BumpPool m_pool; + BoxedHandleUnwrapAndDeletePreserveBoxedMapping m_boxedHandleUnwrapAndDeletePreserveBoxedMapping; + std::optional m_prevSeqno; +}; + +VkDecoder::VkDecoder() : + mImpl(new VkDecoder::Impl()) { } + +VkDecoder::~VkDecoder() = default; + +void VkDecoder::setForSnapshotLoad(bool forSnapshotLoad) { + mImpl->setForSnapshotLoad(forSnapshotLoad); +} + +size_t VkDecoder::decode(void* buf, size_t bufsize, IOStream* stream, + const ProcessResources* processResources, + const VkDecoderContext& context) { + return mImpl->decode(buf, bufsize, stream, processResources, context); +} + +// VkDecoder::Impl::decode to follow +""" % (VULKAN_STREAM_TYPE, VULKAN_STREAM_TYPE) + +decoder_impl_postamble = """ + +} // namespace vk +} // namespace gfxstream + +""" + +READ_STREAM = "vkReadStream" +WRITE_STREAM = "vkStream" + +# Driver workarounds for APIs that don't work well multithreaded +driver_workarounds_global_lock_apis = [ \ + "vkCreatePipelineLayout", + "vkDestroyPipelineLayout", +] + +def emit_param_decl_for_reading(param, cgen): + if param.staticArrExpr: + cgen.stmt( + cgen.makeRichCTypeDecl(param.getForNonConstAccess())) + else: + cgen.stmt( + cgen.makeRichCTypeDecl(param)) + +def emit_unmarshal(typeInfo, param, cgen, output = False, destroy = False, noUnbox = False): + if destroy: + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "", + direction="read", + dynAlloc=True)) + lenAccess = cgen.generalLengthAccess(param) + lenAccessGuard = cgen.generalLengthAccessGuard(param) + if None == lenAccess or "1" == lenAccess: + cgen.stmt("boxed_%s_preserve = %s" % (param.paramName, param.paramName)) + cgen.stmt("%s = unbox_%s(%s)" % (param.paramName, param.typeName, param.paramName)) + else: + if lenAccessGuard is not None: + cgen.beginIf(lenAccessGuard) + cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i") + cgen.stmt("boxed_%s_preserve[i] = %s[i]" % (param.paramName, param.paramName)) + cgen.stmt("((%s*)(%s))[i] = unbox_%s(%s[i])" % (param.typeName, param.paramName, param.typeName, param.paramName)) + cgen.endFor() + if lenAccessGuard is not None: + cgen.endIf() + else: + if noUnbox: + cgen.line("// No unbox for %s" % (param.paramName)) + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "" if (output or noUnbox) else "unbox_", + direction="read", + dynAlloc=True)) + + +def emit_dispatch_unmarshal(typeInfo: VulkanTypeInfo, param: VulkanType, cgen, globalWrapped): + cgen.stmt("// Begin {} wrapped dispatchable handle unboxing for {}".format( + "global" if globalWrapped else "non", + param.paramName)) + + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "", + direction="read", + dynAlloc=True)) + + if not globalWrapped: + cgen.stmt("auto unboxed_%s = unbox_%s(%s)" % + (param.paramName, param.typeName, param.paramName)) + cgen.stmt("auto vk = dispatch_%s(%s)" % + (param.typeName, param.paramName)) + cgen.stmt("// End manual dispatchable handle unboxing for %s" % param.paramName) + + +def emit_transform(typeInfo, param, cgen, variant="tohost"): + res = iterateVulkanType(typeInfo, param, TransformCodegen( + cgen, param.paramName, "m_state", "transform_%s_" % variant, variant)) + if not res: + cgen.stmt("(void)%s" % param.paramName) + + +def emit_marshal(typeInfo, param, cgen, handleMapOverwrites=False): + iterateVulkanType(typeInfo, param, VulkanMarshalingCodegen( + cgen, + WRITE_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + API_PREFIX_MARSHAL, + direction="write", + handleMapOverwrites=handleMapOverwrites)) + + +class DecodingParameters(object): + def __init__(self, api: VulkanAPI): + self.params: list[VulkanType] = [] + self.toRead: list[VulkanType] = [] + self.toWrite: list[VulkanType] = [] + + for i, param in enumerate(api.parameters): + if i == 0 and param.isDispatchableHandleType(): + param.dispatchHandle = True + + if param.isNonDispatchableHandleType() and param.isCreatedBy(api): + param.nonDispatchableHandleCreate = True + + if param.isNonDispatchableHandleType() and param.isDestroyedBy(api): + param.nonDispatchableHandleDestroy = True + + if param.isDispatchableHandleType() and param.isCreatedBy(api): + param.dispatchableHandleCreate = True + + if param.isDispatchableHandleType() and param.isDestroyedBy(api): + param.dispatchableHandleDestroy = True + + self.toRead.append(param) + + if param.possiblyOutput(): + self.toWrite.append(param) + + self.params.append(param) + + +def emit_call_log(api, cgen): + decodingParams = DecodingParameters(api) + paramsToRead = decodingParams.toRead + + cgen.beginIf("m_logCalls") + paramLogFormat = "" + paramLogArgs = [] + for p in paramsToRead: + paramLogFormat += "0x%llx " + for p in paramsToRead: + paramLogArgs.append("(unsigned long long)%s" % (p.paramName)) + cgen.stmt("fprintf(stderr, \"stream %%p: call %s %s\\n\", ioStream, %s)" % (api.name, paramLogFormat, ", ".join(paramLogArgs))) + cgen.endIf() + +def emit_decode_parameters(typeInfo: VulkanTypeInfo, api: VulkanAPI, cgen, globalWrapped=False): + decodingParams = DecodingParameters(api) + + paramsToRead = decodingParams.toRead + + for p in paramsToRead: + emit_param_decl_for_reading(p, cgen) + + for i, p in enumerate(paramsToRead): + lenAccess = cgen.generalLengthAccess(p) + + if p.dispatchHandle: + emit_dispatch_unmarshal(typeInfo, p, cgen, globalWrapped) + else: + destroy = p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy + noUnbox = api.name in ["vkQueueFlushCommandsGOOGLE", "vkQueueFlushCommandsFromAuxMemoryGOOGLE"] and p.paramName == "commandBuffer" + + if p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy: + destroy = True + cgen.stmt("// Begin manual non dispatchable handle destroy unboxing for %s" % p.paramName) + if None == lenAccess or "1" == lenAccess: + cgen.stmt("%s boxed_%s_preserve" % (p.typeName, p.paramName)) + else: + cgen.stmt("%s* boxed_%s_preserve; %s->alloc((void**)&boxed_%s_preserve, %s * sizeof(%s))" % (p.typeName, p.paramName, READ_STREAM, p.paramName, lenAccess, p.typeName)) + + if p.possiblyOutput(): + cgen.stmt("// Begin manual dispatchable handle unboxing for %s" % p.paramName) + cgen.stmt("%s->unsetHandleMapping()" % READ_STREAM) + + emit_unmarshal(typeInfo, p, cgen, output = p.possiblyOutput(), destroy = destroy, noUnbox = noUnbox) + + for p in paramsToRead: + emit_transform(typeInfo, p, cgen, variant="tohost") + + emit_call_log(api, cgen) + +def emit_dispatch_call(api, cgen): + + decodingParams = DecodingParameters(api) + + customParams = [] + + delay = api.name in DELAYED_DECODER_DELETES + + for i, p in enumerate(api.parameters): + customParam = p.paramName + if decodingParams.params[i].dispatchHandle: + customParam = "unboxed_%s" % p.paramName + customParams.append(customParam) + + if delay: + cgen.line("std::function delayed_remove_callback = [vk, %s]() {" % ", ".join(customParams)) + + if api.name in driver_workarounds_global_lock_apis: + if delay: + cgen.stmt("auto state = VkDecoderGlobalState::get()") + cgen.stmt("// state already locked") + else: + cgen.stmt("m_state->lock()") + + cgen.vkApiCall(api, customPrefix="vk->", customParameters=customParams, \ + globalStatePrefix=global_state_prefix, checkForDeviceLost=True, + checkForOutOfMemory=True) + + if api.name in driver_workarounds_global_lock_apis: + if not delay: + cgen.stmt("m_state->unlock()") + # for delayed remove, state is already locked, so we do not need to + # unlock + + if delay: + cgen.line("};") + +def emit_global_state_wrapped_call(api, cgen, context): + if api.name in DELAYED_DECODER_DELETES: + print("Error: Cannot generate a global state wrapped call that is also a delayed delete (yet)"); + raise + + customParams = ["&m_pool"] + list(map(lambda p: p.paramName, api.parameters)) + if context: + customParams += ["context"] + cgen.vkApiCall(api, customPrefix=global_state_prefix, \ + customParameters=customParams, globalStatePrefix=global_state_prefix, \ + checkForDeviceLost=True, checkForOutOfMemory=True) + +def emit_decode_parameters_writeback(typeInfo, api, cgen, autobox=True): + decodingParams = DecodingParameters(api) + + paramsToWrite = decodingParams.toWrite + + cgen.stmt("%s->unsetHandleMapping()" % WRITE_STREAM) + + handleMapOverwrites = False + + for p in paramsToWrite: + emit_transform(typeInfo, p, cgen, variant="fromhost") + + handleMapOverwrites = False + + if p.nonDispatchableHandleCreate or p.dispatchableHandleCreate: + handleMapOverwrites = True + + if autobox and p.nonDispatchableHandleCreate: + cgen.stmt("// Begin auto non dispatchable handle create for %s" % p.paramName) + cgen.stmt("if (%s == VK_SUCCESS) %s->setHandleMapping(&m_boxedHandleCreateMapping)" % \ + (api.getRetVarExpr(), WRITE_STREAM)) + + if (not autobox) and p.nonDispatchableHandleCreate: + cgen.stmt("// Begin manual non dispatchable handle create for %s" % p.paramName) + cgen.stmt("%s->unsetHandleMapping()" % WRITE_STREAM) + + emit_marshal(typeInfo, p, cgen, handleMapOverwrites=handleMapOverwrites) + + if autobox and p.nonDispatchableHandleCreate: + cgen.stmt("// Begin auto non dispatchable handle create for %s" % p.paramName) + cgen.stmt("%s->setHandleMapping(&m_boxedHandleUnwrapMapping)" % WRITE_STREAM) + + if (not autobox) and p.nonDispatchableHandleCreate: + cgen.stmt("// Begin manual non dispatchable handle create for %s" % p.paramName) + cgen.stmt("%s->setHandleMapping(&m_boxedHandleUnwrapMapping)" % WRITE_STREAM) + +def emit_decode_return_writeback(api, cgen): + retTypeName = api.getRetTypeExpr() + if retTypeName != "void": + retVar = api.getRetVarExpr() + cgen.stmt("%s->write(&%s, %s)" % + (WRITE_STREAM, retVar, cgen.sizeofExpr(api.retType))) + +def emit_decode_finish(api, cgen): + decodingParams = DecodingParameters(api) + retTypeName = api.getRetTypeExpr() + paramsToWrite = decodingParams.toWrite + + if retTypeName != "void" or len(paramsToWrite) != 0: + cgen.stmt("%s->commitWrite()" % WRITE_STREAM) + +def emit_destroyed_handle_cleanup(api, cgen): + decodingParams = DecodingParameters(api) + paramsToRead = decodingParams.toRead + + skipDelete = api.name in SKIPPED_DECODER_DELETES + + if skipDelete: + cgen.line("// Skipping handle cleanup for %s" % api.name) + return + + for p in paramsToRead: + if p.dispatchHandle: + pass + else: + lenAccess = cgen.generalLengthAccess(p) + lenAccessGuard = cgen.generalLengthAccess(p) + destroy = p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy + if destroy: + if None == lenAccess or "1" == lenAccess: + if api.name in DELAYED_DECODER_DELETES: + cgen.stmt("delayed_delete_%s(boxed_%s_preserve, unboxed_device, delayed_remove_callback)" % (p.typeName, p.paramName)) + else: + cgen.stmt("delete_%s(boxed_%s_preserve)" % (p.typeName, p.paramName)) + else: + if lenAccessGuard is not None: + cgen.beginIf(lenAccessGuard) + cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i") + if api.name in DELAYED_DECODER_DELETES: + cgen.stmt("delayed_delete_%s(boxed_%s_preserve[i], unboxed_device, delayed_remove_callback)" % (p.typeName, p.paramName)) + else: + cgen.stmt("delete_%s(boxed_%s_preserve[i])" % (p.typeName, p.paramName)) + cgen.endFor() + if lenAccessGuard is not None: + cgen.endIf() + +def emit_pool_free(cgen): + cgen.stmt("%s->clearPool()" % READ_STREAM) + +def emit_seqno_incr(api, cgen): + cgen.stmt("if (queueSubmitWithCommandsEnabled) seqnoPtr->fetch_add(1, std::memory_order_seq_cst)") + +def emit_snapshot(typeInfo, api, cgen): + + cgen.stmt("%s->setReadPos((uintptr_t)(*readStreamPtrPtr) - (uintptr_t)snapshotTraceBegin)" % READ_STREAM) + cgen.stmt("size_t snapshotTraceBytes = %s->endTrace()" % READ_STREAM) + + additionalParams = [ \ + makeVulkanTypeSimple(True, "uint8_t", 1, "snapshotTraceBegin"), + makeVulkanTypeSimple(False, "size_t", 0, "snapshotTraceBytes"), + makeVulkanTypeSimple(False, "android::base::BumpPool", 1, "&m_pool"), + ] + + retTypeName = api.getRetTypeExpr() + if retTypeName != "void": + retVar = api.getRetVarExpr() + additionalParams.append(makeVulkanTypeSimple(False, retTypeName, 0, retVar)) + + paramsForSnapshot = [] + + decodingParams = DecodingParameters(api) + + for p in decodingParams.toRead: + if p.nonDispatchableHandleDestroy or (not p.dispatchHandle and p.dispatchableHandleDestroy): + paramsForSnapshot.append(p.withModifiedName("boxed_%s_preserve" % p.paramName)) + else: + paramsForSnapshot.append(p) + + customParams = additionalParams + paramsForSnapshot + + apiForSnapshot = \ + api.withCustomReturnType(makeVulkanTypeSimple(False, "void", 0, "void")). \ + withCustomParameters(customParams) + + cgen.beginIf("m_state->snapshotsEnabled()") + cgen.vkApiCall(apiForSnapshot, customPrefix="m_state->snapshot()->") + cgen.endIf() + +def emit_decoding(typeInfo, api, cgen, globalWrapped=False, context=False): + isAcquire = api.name in RELAXED_APIS + emit_decode_parameters(typeInfo, api, cgen, globalWrapped) + + if isAcquire: + emit_seqno_incr(api, cgen) + + if globalWrapped: + emit_global_state_wrapped_call(api, cgen, context) + else: + emit_dispatch_call(api, cgen) + + emit_decode_parameters_writeback(typeInfo, api, cgen, autobox=not globalWrapped) + emit_decode_return_writeback(api, cgen) + emit_decode_finish(api, cgen) + emit_snapshot(typeInfo, api, cgen) + emit_destroyed_handle_cleanup(api, cgen) + emit_pool_free(cgen) + + if not isAcquire: + emit_seqno_incr(api, cgen) + +def emit_default_decoding(typeInfo, api, cgen): + emit_decoding(typeInfo, api, cgen) + +def emit_global_state_wrapped_decoding(typeInfo, api, cgen): + emit_decoding(typeInfo, api, cgen, globalWrapped=True) + +def emit_global_state_wrapped_decoding_with_context(typeInfo, api, cgen): + emit_decoding(typeInfo, api, cgen, globalWrapped=True, context=True) + +## Custom decoding definitions################################################## +def decode_vkFlushMappedMemoryRanges(typeInfo: VulkanTypeInfo, api, cgen): + emit_decode_parameters(typeInfo, api, cgen) + + cgen.beginIf("!m_state->usingDirectMapping()") + cgen.stmt("// This is to deal with a deficiency in the encoder,"); + cgen.stmt("// where usingDirectMapping fails to set the proper packet size,"); + cgen.stmt("// meaning we can read off the end of the packet."); + cgen.stmt("uint64_t sizeLeft = end - *readStreamPtrPtr") + cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i") + cgen.beginIf("sizeLeft < sizeof(uint64_t)") + cgen.beginIf("m_prevSeqno") + cgen.stmt("m_prevSeqno = m_prevSeqno.value() - 1") + cgen.endIf() + cgen.stmt("return ptr - (unsigned char*)buf;") + cgen.endIf() + cgen.stmt("auto range = pMemoryRanges[i]") + cgen.stmt("auto memory = pMemoryRanges[i].memory") + cgen.stmt("auto size = pMemoryRanges[i].size") + cgen.stmt("auto offset = pMemoryRanges[i].offset") + cgen.stmt("uint64_t readStream = 0") + cgen.stmt("memcpy(&readStream, *readStreamPtrPtr, sizeof(uint64_t)); *readStreamPtrPtr += sizeof(uint64_t)") + cgen.stmt("sizeLeft -= sizeof(uint64_t)") + cgen.stmt("auto hostPtr = m_state->getMappedHostPointer(memory)") + cgen.stmt("if (!hostPtr && readStream > 0) GFXSTREAM_ABORT(::emugl::FatalError(::emugl::ABORT_REASON_OTHER))") + cgen.stmt("if (!hostPtr) continue") + cgen.beginIf("sizeLeft < readStream") + cgen.beginIf("m_prevSeqno") + cgen.stmt("m_prevSeqno = m_prevSeqno.value() - 1") + cgen.endIf() + cgen.stmt("return ptr - (unsigned char*)buf;") + cgen.endIf() + cgen.stmt("sizeLeft -= readStream") + cgen.stmt("uint8_t* targetRange = hostPtr + offset") + cgen.stmt("memcpy(targetRange, *readStreamPtrPtr, readStream); *readStreamPtrPtr += readStream") + cgen.stmt("packetLen += 8 + readStream") + cgen.endFor() + cgen.endIf() + + emit_dispatch_call(api, cgen) + emit_decode_parameters_writeback(typeInfo, api, cgen) + emit_decode_return_writeback(api, cgen) + emit_decode_finish(api, cgen) + emit_snapshot(typeInfo, api, cgen); + emit_pool_free(cgen) + emit_seqno_incr(api, cgen) + +def decode_vkInvalidateMappedMemoryRanges(typeInfo, api, cgen): + emit_decode_parameters(typeInfo, api, cgen) + emit_dispatch_call(api, cgen) + emit_decode_parameters_writeback(typeInfo, api, cgen) + emit_decode_return_writeback(api, cgen) + + cgen.beginIf("!m_state->usingDirectMapping()") + cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i") + cgen.stmt("auto range = pMemoryRanges[i]") + cgen.stmt("auto memory = range.memory") + cgen.stmt("auto size = range.size") + cgen.stmt("auto offset = range.offset") + cgen.stmt("auto hostPtr = m_state->getMappedHostPointer(memory)") + cgen.stmt("auto actualSize = size == VK_WHOLE_SIZE ? m_state->getDeviceMemorySize(memory) : size") + cgen.stmt("uint64_t writeStream = 0") + cgen.stmt("if (!hostPtr) { %s->write(&writeStream, sizeof(uint64_t)); continue; }" % WRITE_STREAM) + cgen.stmt("uint8_t* targetRange = hostPtr + offset") + cgen.stmt("writeStream = actualSize") + cgen.stmt("%s->write(&writeStream, sizeof(uint64_t))" % WRITE_STREAM) + cgen.stmt("%s->write(targetRange, actualSize)" % WRITE_STREAM) + cgen.endFor() + cgen.endIf() + + emit_decode_finish(api, cgen) + emit_snapshot(typeInfo, api, cgen); + emit_pool_free(cgen) + emit_seqno_incr(api, cgen) + +def decode_unsupported_api(typeInfo, api, cgen): + cgen.line(f"// Decoding {api.name} is not supported. This should not run.") + cgen.stmt(f"fprintf(stderr, \"stream %p: fatal: decoding unsupported API {api.name}\\n\", ioStream)"); + cgen.stmt("__builtin_trap()") + +custom_decodes = { + "vkEnumerateInstanceVersion" : emit_global_state_wrapped_decoding, + "vkCreateInstance" : emit_global_state_wrapped_decoding, + "vkDestroyInstance" : emit_global_state_wrapped_decoding, + "vkEnumeratePhysicalDevices" : emit_global_state_wrapped_decoding, + + "vkGetPhysicalDeviceFeatures" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceFeatures2" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceFeatures2KHR" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceFormatProperties" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceFormatProperties2" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceFormatProperties2KHR" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceImageFormatProperties" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceImageFormatProperties2" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceImageFormatProperties2KHR" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceProperties" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceProperties2" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceProperties2KHR" : emit_global_state_wrapped_decoding, + + "vkGetPhysicalDeviceMemoryProperties" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceMemoryProperties2" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceMemoryProperties2KHR" : emit_global_state_wrapped_decoding, + + "vkGetPhysicalDeviceExternalSemaphoreProperties" : emit_global_state_wrapped_decoding, + "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : emit_global_state_wrapped_decoding, + + "vkEnumerateDeviceExtensionProperties" : emit_global_state_wrapped_decoding, + + "vkCreateBuffer" : emit_global_state_wrapped_decoding, + "vkDestroyBuffer" : emit_global_state_wrapped_decoding, + + "vkBindBufferMemory" : emit_global_state_wrapped_decoding, + "vkBindBufferMemory2" : emit_global_state_wrapped_decoding, + "vkBindBufferMemory2KHR" : emit_global_state_wrapped_decoding, + + "vkCreateDevice" : emit_global_state_wrapped_decoding, + "vkGetDeviceQueue" : emit_global_state_wrapped_decoding, + "vkDestroyDevice" : emit_global_state_wrapped_decoding, + + "vkGetDeviceQueue2" : emit_global_state_wrapped_decoding, + + "vkBindImageMemory" : emit_global_state_wrapped_decoding, + "vkBindImageMemory2" : emit_global_state_wrapped_decoding, + "vkBindImageMemory2KHR" : emit_global_state_wrapped_decoding, + + "vkCreateImage" : emit_global_state_wrapped_decoding, + "vkCreateImageView" : emit_global_state_wrapped_decoding, + "vkCreateSampler" : emit_global_state_wrapped_decoding, + "vkDestroyImage" : emit_global_state_wrapped_decoding, + "vkDestroyImageView" : emit_global_state_wrapped_decoding, + "vkDestroySampler" : emit_global_state_wrapped_decoding, + "vkCmdCopyBufferToImage" : emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage" : emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer" : emit_global_state_wrapped_decoding, + "vkCmdCopyBufferToImage2" : emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage2" : emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer2" : emit_global_state_wrapped_decoding, + "vkGetImageMemoryRequirements" : emit_global_state_wrapped_decoding, + "vkGetImageMemoryRequirements2" : emit_global_state_wrapped_decoding, + "vkGetImageMemoryRequirements2KHR" : emit_global_state_wrapped_decoding, + "vkGetBufferMemoryRequirements" : emit_global_state_wrapped_decoding, + "vkGetBufferMemoryRequirements2": emit_global_state_wrapped_decoding, + "vkGetBufferMemoryRequirements2KHR": emit_global_state_wrapped_decoding, + + "vkCreateDescriptorSetLayout" : emit_global_state_wrapped_decoding, + "vkDestroyDescriptorSetLayout" : emit_global_state_wrapped_decoding, + "vkCreateDescriptorPool" : emit_global_state_wrapped_decoding, + "vkDestroyDescriptorPool" : emit_global_state_wrapped_decoding, + "vkResetDescriptorPool" : emit_global_state_wrapped_decoding, + "vkAllocateDescriptorSets" : emit_global_state_wrapped_decoding, + "vkFreeDescriptorSets" : emit_global_state_wrapped_decoding, + + "vkUpdateDescriptorSets" : emit_global_state_wrapped_decoding, + + "vkCreateShaderModule": emit_global_state_wrapped_decoding, + "vkDestroyShaderModule": emit_global_state_wrapped_decoding, + "vkCreatePipelineCache": emit_global_state_wrapped_decoding, + "vkDestroyPipelineCache": emit_global_state_wrapped_decoding, + "vkCreateGraphicsPipelines": emit_global_state_wrapped_decoding, + "vkDestroyPipeline": emit_global_state_wrapped_decoding, + + "vkAllocateMemory" : emit_global_state_wrapped_decoding, + "vkFreeMemory" : emit_global_state_wrapped_decoding, + "vkMapMemory" : emit_global_state_wrapped_decoding, + "vkUnmapMemory" : emit_global_state_wrapped_decoding, + "vkFlushMappedMemoryRanges" : decode_vkFlushMappedMemoryRanges, + "vkInvalidateMappedMemoryRanges" : decode_vkInvalidateMappedMemoryRanges, + + "vkAllocateCommandBuffers" : emit_global_state_wrapped_decoding, + "vkCmdExecuteCommands" : emit_global_state_wrapped_decoding, + "vkQueueSubmit" : emit_global_state_wrapped_decoding, + "vkQueueSubmit2" : emit_global_state_wrapped_decoding, + "vkQueueWaitIdle" : emit_global_state_wrapped_decoding, + "vkBeginCommandBuffer" : emit_global_state_wrapped_decoding_with_context, + "vkEndCommandBuffer" : emit_global_state_wrapped_decoding_with_context, + "vkResetCommandBuffer" : emit_global_state_wrapped_decoding, + "vkFreeCommandBuffers" : emit_global_state_wrapped_decoding, + "vkCreateCommandPool" : emit_global_state_wrapped_decoding, + "vkDestroyCommandPool" : emit_global_state_wrapped_decoding, + "vkResetCommandPool" : emit_global_state_wrapped_decoding, + "vkCmdPipelineBarrier" : emit_global_state_wrapped_decoding, + "vkCmdBindPipeline" : emit_global_state_wrapped_decoding, + "vkCmdBindDescriptorSets" : emit_global_state_wrapped_decoding, + + "vkCreateRenderPass" : emit_global_state_wrapped_decoding, + "vkCreateRenderPass2" : emit_global_state_wrapped_decoding, + "vkCreateRenderPass2KHR" : emit_global_state_wrapped_decoding, + "vkDestroyRenderPass" : emit_global_state_wrapped_decoding, + "vkCreateFramebuffer" : emit_global_state_wrapped_decoding, + "vkDestroyFramebuffer" : emit_global_state_wrapped_decoding, + + "vkCreateSamplerYcbcrConversion": emit_global_state_wrapped_decoding, + "vkDestroySamplerYcbcrConversion": emit_global_state_wrapped_decoding, + + # VK_ANDROID_native_buffer + "vkGetSwapchainGrallocUsageANDROID" : emit_global_state_wrapped_decoding, + "vkGetSwapchainGrallocUsage2ANDROID" : emit_global_state_wrapped_decoding, + "vkAcquireImageANDROID" : emit_global_state_wrapped_decoding, + "vkQueueSignalReleaseImageANDROID" : emit_global_state_wrapped_decoding, + + "vkCreateSemaphore" : emit_global_state_wrapped_decoding, + "vkGetSemaphoreFdKHR" : emit_global_state_wrapped_decoding, + "vkImportSemaphoreFdKHR" : emit_global_state_wrapped_decoding, + "vkDestroySemaphore" : emit_global_state_wrapped_decoding, + + "vkCreateFence" : emit_global_state_wrapped_decoding, + "vkResetFences" : emit_global_state_wrapped_decoding, + "vkDestroyFence" : emit_global_state_wrapped_decoding, + + # VK_GOOGLE_gfxstream + "vkFreeMemorySyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkMapMemoryIntoAddressSpaceGOOGLE" : emit_global_state_wrapped_decoding, + "vkGetMemoryHostAddressInfoGOOGLE" : emit_global_state_wrapped_decoding, + "vkGetBlobGOOGLE" : emit_global_state_wrapped_decoding, + + # Descriptor update templates + "vkCreateDescriptorUpdateTemplate" : emit_global_state_wrapped_decoding, + "vkCreateDescriptorUpdateTemplateKHR" : emit_global_state_wrapped_decoding, + "vkDestroyDescriptorUpdateTemplate" : emit_global_state_wrapped_decoding, + "vkDestroyDescriptorUpdateTemplateKHR" : emit_global_state_wrapped_decoding, + "vkUpdateDescriptorSetWithTemplateSizedGOOGLE" : emit_global_state_wrapped_decoding, + "vkUpdateDescriptorSetWithTemplateSized2GOOGLE" : emit_global_state_wrapped_decoding, + + # VK_GOOGLE_gfxstream + "vkBeginCommandBufferAsyncGOOGLE" : emit_global_state_wrapped_decoding_with_context, + "vkEndCommandBufferAsyncGOOGLE" : emit_global_state_wrapped_decoding_with_context, + "vkResetCommandBufferAsyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkCommandBufferHostSyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkCreateImageWithRequirementsGOOGLE" : emit_global_state_wrapped_decoding, + "vkCreateBufferWithRequirementsGOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueHostSyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueSubmitAsyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueSubmitAsync2GOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueWaitIdleAsyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueBindSparseAsyncGOOGLE" : emit_global_state_wrapped_decoding, + "vkGetLinearImageLayoutGOOGLE" : emit_global_state_wrapped_decoding, + "vkGetLinearImageLayout2GOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueFlushCommandsGOOGLE" : emit_global_state_wrapped_decoding_with_context, + "vkQueueFlushCommandsFromAuxMemoryGOOGLE" : emit_global_state_wrapped_decoding_with_context, + "vkQueueCommitDescriptorSetUpdatesGOOGLE" : emit_global_state_wrapped_decoding, + "vkCollectDescriptorPoolIdsGOOGLE" : emit_global_state_wrapped_decoding, + "vkQueueSignalReleaseImageANDROIDAsyncGOOGLE" : emit_global_state_wrapped_decoding, + + "vkQueueBindSparse" : emit_global_state_wrapped_decoding, + + # VK_KHR_xcb_surface + "vkCreateXcbSurfaceKHR": decode_unsupported_api, + "vkGetPhysicalDeviceXcbPresentationSupportKHR": decode_unsupported_api, + + # VK_EXT_metal_surface + "vkCreateMetalSurfaceEXT": decode_unsupported_api, + + # VK_KHR_sampler_ycbcr_conversion + "vkCreateSamplerYcbcrConversionKHR": emit_global_state_wrapped_decoding, + "vkDestroySamplerYcbcrConversionKHR": emit_global_state_wrapped_decoding, + + #VK_KHR_copy_commands2 + "vkCmdCopyBufferToImage2KHR" : emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage2KHR" : emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer2KHR" : emit_global_state_wrapped_decoding, +} + +class VulkanDecoder(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + self.typeInfo: VulkanTypeInfo = typeInfo + self.cgen = CodeGen() + + def onBegin(self,): + self.module.appendImpl( + "#define MAX_PACKET_LENGTH %s\n" % MAX_PACKET_LENGTH) + self.module.appendHeader(decoder_decl_preamble) + self.module.appendImpl(decoder_impl_preamble) + + self.module.appendImpl( + """ +size_t VkDecoder::Impl::decode(void* buf, size_t len, IOStream* ioStream, + const ProcessResources* processResources, + const VkDecoderContext& context) +""") + + self.cgen.beginBlock() # function body + + self.cgen.stmt("const char* processName = context.processName") + self.cgen.stmt("auto& gfx_logger = *context.gfxApiLogger") + self.cgen.stmt("auto* healthMonitor = context.healthMonitor") + self.cgen.stmt("auto& metricsLogger = *context.metricsLogger") + self.cgen.stmt("if (len < 8) return 0") + self.cgen.stmt("bool queueSubmitWithCommandsEnabled = feature_is_enabled(kFeature_VulkanQueueSubmitWithCommands)") + self.cgen.stmt("unsigned char *ptr = (unsigned char *)buf") + self.cgen.stmt("const unsigned char* const end = (const unsigned char*)buf + len") + + self.cgen.beginIf("m_forSnapshotLoad") + self.cgen.stmt("ptr += m_state->setCreatedHandlesForSnapshotLoad(ptr)"); + self.cgen.endIf() + self.cgen.line("while (end - ptr >= 8)") + self.cgen.beginBlock() # while loop + + self.cgen.stmt("uint32_t opcode = *(uint32_t *)ptr") + self.cgen.stmt("uint32_t packetLen = *(uint32_t *)(ptr + 4)") + self.cgen.line(""" + // packetLen should be at least 8 (op code and packet length) and should not be excessively large + if (packetLen < 8 || packetLen > MAX_PACKET_LENGTH) { + WARN("Bad packet length %d detected, decode may fail", packetLen); + metricsLogger.logMetricEvent(MetricEventBadPacketLength{ .len = packetLen }); + } + """) + self.cgen.stmt("if (end - ptr < packetLen) return ptr - (unsigned char*)buf") + self.cgen.stmt("gfx_logger.record(ptr, std::min(size_t(packetLen + 8), size_t(end - ptr)))") + + self.cgen.stmt("stream()->setStream(ioStream)") + self.cgen.stmt("VulkanStream* %s = stream()" % WRITE_STREAM) + self.cgen.stmt("VulkanMemReadingStream* %s = readStream()" % READ_STREAM) + self.cgen.stmt("%s->setBuf((uint8_t*)(ptr + 8))" % READ_STREAM) + self.cgen.stmt("uint8_t* readStreamPtr = %s->getBuf(); uint8_t** readStreamPtrPtr = &readStreamPtr" % READ_STREAM) + self.cgen.stmt("uint8_t* snapshotTraceBegin = %s->beginTrace()" % READ_STREAM) + self.cgen.stmt("%s->setHandleMapping(&m_boxedHandleUnwrapMapping)" % READ_STREAM) + self.cgen.line(""" + std::unique_ptr executionData = + std::make_unique(); + if (healthMonitor) { + executionData->insert( + {{"packet_length", std::to_string(packetLen)}, + {"opcode", std::to_string(opcode)}}); + if (processName) { + executionData->insert( + {{"renderthread_guest_process", std::string(processName)}}); + } + if (m_prevSeqno) { + executionData->insert({{"previous_seqno", std::to_string(m_prevSeqno.value())}}); + } + } + + std::atomic* seqnoPtr = processResources ? + processResources->getSequenceNumberPtr() : nullptr; + + if (queueSubmitWithCommandsEnabled && ((opcode >= OP_vkFirst && opcode < OP_vkLast) || (opcode >= OP_vkFirst_old && opcode < OP_vkLast_old))) { + uint32_t seqno; + memcpy(&seqno, *readStreamPtrPtr, sizeof(uint32_t)); *readStreamPtrPtr += sizeof(uint32_t); + if (healthMonitor) executionData->insert({{"seqno", std::to_string(seqno)}}); + if (m_prevSeqno && seqno == m_prevSeqno.value()) { + WARN( + "Seqno %d is the same as previously processed on thread %d. It might be a " + "duplicate command.", + seqno, getCurrentThreadId()); + metricsLogger.logMetricEvent(MetricEventDuplicateSequenceNum{ .opcode = opcode }); + } + if (seqnoPtr && !m_forSnapshotLoad) { + { + auto seqnoWatchdog = + WATCHDOG_BUILDER(healthMonitor, + "RenderThread seqno loop") + .setHangType(EventHangMetadata::HangType::kRenderThread) + .setAnnotations(std::make_unique(*executionData)) + /* Data gathered if this hangs*/ + .setOnHangCallback([=]() { + auto annotations = std::make_unique(); + annotations->insert({{"seqnoPtr", std::to_string(seqnoPtr->load(std::memory_order_seq_cst))}}); + return annotations; + }) + .build(); + while ((seqno - seqnoPtr->load(std::memory_order_seq_cst) != 1)) { + #if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) + _mm_pause(); + #elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) + __asm__ __volatile__("pause;"); + #endif + } + m_prevSeqno = seqno; + } + } + } + """) + + self.cgen.line(""" + gfx_logger.recordCommandExecution(); + """) + + self.cgen.line(""" + auto executionWatchdog = + WATCHDOG_BUILDER(healthMonitor, "RenderThread VkDecoder command execution") + .setHangType(EventHangMetadata::HangType::kRenderThread) + .setAnnotations(std::move(executionData)) + .build(); + """) + + self.cgen.stmt("auto vk = m_vk") + + self.cgen.line("switch (opcode)") + self.cgen.beginBlock() # switch stmt + + self.module.appendImpl(self.cgen.swapCode()) + + def onGenCmd(self, cmdinfo, name, alias): + typeInfo = self.typeInfo + cgen = self.cgen + api: VulkanAPI = typeInfo.apis[name] + + cgen.line("case OP_%s:" % name) + cgen.beginBlock() + cgen.stmt("android::base::beginTrace(\"%s decode\")" % name) + + if api.name in custom_decodes.keys(): + custom_decodes[api.name](typeInfo, api, cgen) + else: + emit_default_decoding(typeInfo, api, cgen) + + cgen.stmt("android::base::endTrace()") + cgen.stmt("break") + cgen.endBlock() + self.module.appendImpl(self.cgen.swapCode()) + + def onEnd(self,): + self.cgen.line("default:") + self.cgen.beginBlock() + self.cgen.stmt("m_pool.freeAll()") + self.cgen.stmt("return ptr - (unsigned char *)buf") + self.cgen.endBlock() + + self.cgen.endBlock() # switch stmt + + self.cgen.stmt("ptr += packetLen") + self.cgen.endBlock() # while loop + + self.cgen.beginIf("m_forSnapshotLoad") + self.cgen.stmt("m_state->clearCreatedHandlesForSnapshotLoad()"); + self.cgen.endIf() + + self.cgen.stmt("m_pool.freeAll()") + self.cgen.stmt("return ptr - (unsigned char*)buf;") + self.cgen.endBlock() # function body + self.module.appendImpl(self.cgen.swapCode()) + self.module.appendImpl(decoder_impl_postamble) diff --git a/src/gfxstream/codegen/scripts/cereal/decodersnapshot.py b/src/gfxstream/codegen/scripts/cereal/decodersnapshot.py new file mode 100644 index 00000000000..4ff97a1d2c4 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/decodersnapshot.py @@ -0,0 +1,267 @@ +from .common.codegen import CodeGen, VulkanWrapperGenerator, VulkanAPIWrapper +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, DISPATCHABLE_HANDLE_TYPES, NON_DISPATCHABLE_HANDLE_TYPES + +from .transform import TransformCodegen, genTransformsForVulkanType + +from .wrapperdefs import API_PREFIX_MARSHAL +from .wrapperdefs import API_PREFIX_UNMARSHAL +from .wrapperdefs import VULKAN_STREAM_TYPE + +from copy import copy + +decoder_snapshot_decl_preamble = """ + +namespace android { +namespace base { +class BumpPool; +class Stream; +} // namespace base { +} // namespace android { + +class VkDecoderSnapshot { +public: + VkDecoderSnapshot(); + ~VkDecoderSnapshot(); + + void save(android::base::Stream* stream); + void load(android::base::Stream* stream, emugl::GfxApiLogger& gfx_logger, + emugl::HealthMonitor<>* healthMonitor); +""" + +decoder_snapshot_decl_postamble = """ +private: + class Impl; + std::unique_ptr mImpl; + +}; +""" + +decoder_snapshot_impl_preamble =""" + +using namespace gfxstream::vk; +using emugl::GfxApiLogger; +using emugl::HealthMonitor; + +class VkDecoderSnapshot::Impl { +public: + Impl() { } + + void save(android::base::Stream* stream) { + mReconstruction.save(stream); + } + + void load(android::base::Stream* stream, GfxApiLogger& gfx_logger, + HealthMonitor<>* healthMonitor) { + mReconstruction.load(stream, gfx_logger, healthMonitor); + } + +""" + +decoder_snapshot_impl_postamble = """ +private: + android::base::Lock mLock; + VkReconstruction mReconstruction; +}; + +VkDecoderSnapshot::VkDecoderSnapshot() : + mImpl(new VkDecoderSnapshot::Impl()) { } + +void VkDecoderSnapshot::save(android::base::Stream* stream) { + mImpl->save(stream); +} + +void VkDecoderSnapshot::load(android::base::Stream* stream, GfxApiLogger& gfx_logger, + HealthMonitor<>* healthMonitor) { + mImpl->load(stream, gfx_logger, healthMonitor); +} + +VkDecoderSnapshot::~VkDecoderSnapshot() = default; +""" + +AUXILIARY_SNAPSHOT_API_BASE_PARAM_COUNT = 3 + +AUXILIARY_SNAPSHOT_API_PARAM_NAMES = [ + "input_result", +] + +# Vulkan handle dependencies. +# (a, b): a depends on b +SNAPSHOT_HANDLE_DEPENDENCIES = [ + # Dispatchable handle types + ("VkCommandBuffer", "VkCommandPool"), + ("VkCommandPool", "VkDevice"), + ("VkQueue", "VkDevice"), + ("VkDevice", "VkPhysicalDevice"), + ("VkPhysicalDevice", "VkInstance")] + \ + list(map(lambda handleType : (handleType, "VkDevice"), NON_DISPATCHABLE_HANDLE_TYPES)) + +handleDependenciesDict = dict(SNAPSHOT_HANDLE_DEPENDENCIES) + +def extract_deps_vkAllocateCommandBuffers(param, access, lenExpr, api, cgen): + cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \ + (access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkCommandPool(pAllocateInfo->commandPool)")) + +specialCaseDependencyExtractors = { + "vkAllocateCommandBuffers" : extract_deps_vkAllocateCommandBuffers, +} + +apiSequences = { + "vkAllocateMemory" : ["vkAllocateMemory", "vkMapMemoryIntoAddressSpaceGOOGLE"] +} + +apiModifies = { + "vkMapMemoryIntoAddressSpaceGOOGLE" : ["memory"], +} + +def is_modify_operation(api, param): + if api.name in apiModifies: + if param.paramName in apiModifies[api.name]: + return True + return False + +def emit_impl(typeInfo, api, cgen): + + cgen.line("// TODO: Implement") + + for p in api.parameters: + if not (p.isHandleType): + continue + + lenExpr = cgen.generalLengthAccess(p) + lenAccessGuard = cgen.generalLengthAccessGuard(p) + + if lenExpr is None: + lenExpr = "1" + + if p.pointerIndirectionLevels > 0: + access = p.paramName + else: + access = "(&%s)" % p.paramName + + if p.isCreatedBy(api): + cgen.stmt("android::base::AutoLock lock(mLock)") + cgen.line("// %s create" % p.paramName) + cgen.stmt("mReconstruction.addHandles((const uint64_t*)%s, %s)" % (access, lenExpr)); + + if p.typeName in handleDependenciesDict: + dependsOnType = handleDependenciesDict[p.typeName]; + for p2 in api.parameters: + if p2.typeName == dependsOnType: + cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % (access, lenExpr, p2.paramName)) + if api.name in specialCaseDependencyExtractors: + specialCaseDependencyExtractors[api.name](p, access, lenExpr, api, cgen) + + cgen.stmt("if (!%s) return" % access) + cgen.stmt("auto apiHandle = mReconstruction.createApiInfo()") + cgen.stmt("auto apiInfo = mReconstruction.getApiInfo(apiHandle)") + cgen.stmt("mReconstruction.setApiTrace(apiInfo, OP_%s, snapshotTraceBegin, snapshotTraceBytes)" % api.name) + if lenAccessGuard is not None: + cgen.beginIf(lenAccessGuard) + cgen.stmt("mReconstruction.forEachHandleAddApi((const uint64_t*)%s, %s, apiHandle)" % (access, lenExpr)) + cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiHandle, (const uint64_t*)%s, %s)" % (access, lenExpr)) + if lenAccessGuard is not None: + cgen.endIf() + + if p.isDestroyedBy(api): + cgen.stmt("android::base::AutoLock lock(mLock)") + cgen.line("// %s destroy" % p.paramName) + if lenAccessGuard is not None: + cgen.beginIf(lenAccessGuard) + cgen.stmt("mReconstruction.removeHandles((const uint64_t*)%s, %s)" % (access, lenExpr)); + if lenAccessGuard is not None: + cgen.endIf() + + if is_modify_operation(api, p): + cgen.stmt("android::base::AutoLock lock(mLock)") + cgen.line("// %s modify" % p.paramName) + cgen.stmt("auto apiHandle = mReconstruction.createApiInfo()") + cgen.stmt("auto apiInfo = mReconstruction.getApiInfo(apiHandle)") + cgen.stmt("mReconstruction.setApiTrace(apiInfo, OP_%s, snapshotTraceBegin, snapshotTraceBytes)" % api.name) + if lenAccessGuard is not None: + cgen.beginIf(lenAccessGuard) + cgen.beginFor("uint32_t i = 0", "i < %s" % lenExpr, "++i") + if p.isNonDispatchableHandleType(): + cgen.stmt("%s boxed = unboxed_to_boxed_non_dispatchable_%s(%s[i])" % (p.typeName, p.typeName, access)) + else: + cgen.stmt("%s boxed = unboxed_to_boxed_%s(%s[i])" % (p.typeName, p.typeName, access)) + cgen.stmt("mReconstruction.forEachHandleAddModifyApi((const uint64_t*)(&boxed), 1, apiHandle)") + cgen.endFor() + if lenAccessGuard is not None: + cgen.endIf() + +def emit_passthrough_to_impl(typeInfo, api, cgen): + cgen.vkApiCall(api, customPrefix = "mImpl->") + +class VulkanDecoderSnapshot(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.typeInfo = typeInfo + + self.cgenHeader = CodeGen() + self.cgenHeader.incrIndent() + + self.cgenImpl = CodeGen() + + self.currentFeature = None + + self.feature_apis = [] + + def onBegin(self,): + self.module.appendHeader(decoder_snapshot_decl_preamble) + self.module.appendImpl(decoder_snapshot_impl_preamble) + + def onBeginFeature(self, featureName, featureType): + VulkanWrapperGenerator.onBeginFeature(self, featureName, featureType) + self.currentFeature = featureName + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + api = self.typeInfo.apis[name] + + additionalParams = [ \ + makeVulkanTypeSimple(True, "uint8_t", 1, "snapshotTraceBegin"), + makeVulkanTypeSimple(False, "size_t", 0, "snapshotTraceBytes"), + makeVulkanTypeSimple(False, "android::base::BumpPool", 1, "pool"),] + + if api.retType.typeName != "void": + additionalParams.append( \ + makeVulkanTypeSimple(False, api.retType.typeName, 0, "input_result")) + + apiForSnapshot = \ + api.withCustomParameters( \ + additionalParams + \ + api.parameters).withCustomReturnType( \ + makeVulkanTypeSimple(False, "void", 0, "void")) + + self.feature_apis.append((self.currentFeature, apiForSnapshot)) + + self.cgenHeader.stmt(self.cgenHeader.makeFuncProto(apiForSnapshot)) + self.module.appendHeader(self.cgenHeader.swapCode()) + + self.cgenImpl.emitFuncImpl( \ + apiForSnapshot, lambda cgen: emit_impl(self.typeInfo, apiForSnapshot, cgen)) + self.module.appendImpl(self.cgenImpl.swapCode()) + + def onEnd(self,): + self.module.appendHeader(decoder_snapshot_decl_postamble) + self.module.appendImpl(decoder_snapshot_impl_postamble) + self.cgenHeader.decrIndent() + + for feature, api in self.feature_apis: + if feature is not None: + self.cgenImpl.line("#ifdef %s" % feature) + + apiImplShell = \ + api.withModifiedName("VkDecoderSnapshot::" + api.name) + + self.cgenImpl.emitFuncImpl( \ + apiImplShell, lambda cgen: emit_passthrough_to_impl(self.typeInfo, api, cgen)) + + if feature is not None: + self.cgenImpl.line("#endif") + + self.module.appendImpl(self.cgenImpl.swapCode()) + diff --git a/src/gfxstream/codegen/scripts/cereal/deepcopy.py b/src/gfxstream/codegen/scripts/cereal/deepcopy.py new file mode 100644 index 00000000000..52842e994b4 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/deepcopy.py @@ -0,0 +1,383 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_API_NAME + +class DeepcopyCodegen(VulkanTypeIterator): + def __init__(self, cgen, inputVars, poolVarName, rootVarName, prefix, skipValues=False): + self.cgen = cgen + self.inputVars = inputVars + self.prefix = prefix + self.poolVarName = poolVarName + self.rootVarName = rootVarName + self.skipValues = skipValues + + def makeAccess(varName, asPtr = True): + return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr) + + def makeLengthAccess(varName): + return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName) + + def makeLengthAccessGuard(varName): + return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName) + + self.exprAccessorLhs = makeAccess(self.inputVars[0]) + self.exprAccessorRhs = makeAccess(self.inputVars[1]) + + self.exprAccessorValueLhs = makeAccess(self.inputVars[0], asPtr = False) + self.exprAccessorValueRhs = makeAccess(self.inputVars[1], asPtr = False) + + self.lenAccessorLhs = makeLengthAccess(self.inputVars[0]) + self.lenAccessorRhs = makeLengthAccess(self.inputVars[1]) + + self.lenAccessorGuardLhs = makeLengthAccessGuard(self.inputVars[0]) + self.lenAccessorGuardRhs = makeLengthAccessGuard(self.inputVars[1]) + + self.checked = False + + def needSkip(self, vulkanType): + return False + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def makeNonConstCastForCopy(self, access, vulkanType): + if vulkanType.staticArrExpr: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + elif vulkanType.accessibleAsPointer(): + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access) + else: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + return casted + + def makeAllocBytesExpr(self, lenAccess, vulkanType): + sizeof = self.cgen.sizeofExpr( \ + vulkanType.getForValueAccess()) + if lenAccess: + bytesExpr = "%s * %s" % (lenAccess, sizeof) + else: + bytesExpr = sizeof + + return bytesExpr + + def onCheck(self, vulkanType): + pass + + def endCheck(self, vulkanType): + pass + + def onCompoundType(self, vulkanType): + + if self.needSkip(vulkanType): + self.cgen.line("// TODO: Unsupported : %s" % + self.cgen.makeCTypeDecl(vulkanType)) + return + + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + lenAccessRhs = self.lenAccessorRhs(vulkanType) + + lenAccessorGuardLhs = self.lenAccessorGuardLhs(vulkanType) + lenAccessorGuardRhs = self.lenAccessorGuardRhs(vulkanType) + + isPtr = vulkanType.pointerIndirectionLevels > 0 + + if lenAccessorGuardLhs is not None: + self.cgen.beginIf(lenAccessorGuardLhs) + + if isPtr: + self.cgen.stmt("%s = nullptr" % accessRhs) + self.cgen.beginIf(accessLhs) + + self.cgen.stmt( \ + "%s = %s%s->alloc(%s)" % \ + (accessRhs, self.makeCastExpr(vulkanType.getForNonConstAccess()), + self.poolVarName, self.makeAllocBytesExpr(lenAccessLhs, vulkanType))) + + if lenAccessLhs is not None: + + loopVar = "i" + accessLhs = "%s + %s" % (accessLhs, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs) + forIncr = "++%s" % loopVar + + if isPtr: + # Avoid generating a self-assign. + if lenAccessRhs != lenAccessLhs: + self.cgen.stmt("%s = %s" % (lenAccessRhs, lenAccessLhs)) + + accessRhs = "%s + %s" % (accessRhs, loopVar) + self.cgen.beginFor(forInit, forCond, forIncr) + + + accessRhsCasted = self.makeNonConstCastForCopy(accessRhs, vulkanType) + + self.cgen.funcCall(None, self.prefix + vulkanType.typeName, + [self.poolVarName, self.rootVarName, accessLhs, accessRhsCasted]) + + if lenAccessLhs is not None: + self.cgen.endFor() + + if isPtr: + self.cgen.endIf() + + if lenAccessorGuardLhs is not None: + self.cgen.endIf() + + def onString(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + self.cgen.stmt("%s = nullptr" % accessRhs) + self.cgen.beginIf(accessLhs) + + self.cgen.stmt( \ + "%s = %s->strDup(%s)" % \ + (accessRhs, + self.poolVarName, + accessLhs)) + + self.cgen.endIf() + + def onStringArray(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + + self.cgen.stmt("%s = nullptr" % accessRhs) + self.cgen.beginIf("%s && %s" % (accessLhs, lenAccessLhs)) + + self.cgen.stmt( \ + "%s = %s->strDupArray(%s, %s)" % \ + (accessRhs, + self.poolVarName, + accessLhs, + lenAccessLhs)) + + self.cgen.endIf() + + def onStaticArr(self, vulkanType): + accessLhs = self.exprAccessorValueLhs(vulkanType) + accessRhs = self.exprAccessorValueRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + + bytesExpr = self.makeAllocBytesExpr(lenAccessLhs, vulkanType) + self.cgen.stmt("memcpy(%s, %s, %s)" % (accessRhs, accessLhs, bytesExpr)) + + def onStructExtension(self, vulkanType): + + lhs = self.exprAccessorLhs(vulkanType) + rhs = self.exprAccessorRhs(vulkanType) + + rhsExpr = "(%s)(%s)" % ("void*", rhs) + + nextVar = "from_%s" % vulkanType.paramName + sizeVar = "%s_size" % vulkanType.paramName + + self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" % + self.rootVarName) + self.cgen.stmt("%s = from->sType" % self.rootVarName) + self.cgen.endIf() + + self.cgen.stmt("const void* %s = %s" % (nextVar, self.inputVars[0])) + self.cgen.stmt("size_t %s = 0u" % sizeVar) + self.cgen.beginWhile("!%s && %s" % (sizeVar, nextVar)) + self.cgen.stmt("%s = static_cast(%s)->%s" % ( + nextVar, nextVar, vulkanType.paramName + )) + self.cgen.stmt("%s = %s(%s, %s)" % ( + sizeVar, EXTENSION_SIZE_API_NAME, self.rootVarName, nextVar)) + self.cgen.endWhile() + + self.cgen.stmt("%s = nullptr" % rhs) + + self.cgen.beginIf(sizeVar) + + self.cgen.stmt( \ + "%s = %s%s->alloc(%s)" % \ + (rhs, self.makeCastExpr(vulkanType.getForNonConstAccess()), self.poolVarName, sizeVar)) + + self.cgen.funcCall(None, self.prefix + "extension_struct", + [self.poolVarName, self.rootVarName, nextVar, rhsExpr]) + + self.cgen.endIf() + + def onPointer(self, vulkanType): + + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + if self.needSkip(vulkanType): + self.cgen.stmt("%s = nullptr" % accessRhs) + return + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + + self.cgen.stmt("%s = nullptr" % accessRhs) + self.cgen.beginIf(accessLhs) + + bytesExpr = self.makeAllocBytesExpr(lenAccessLhs, vulkanType) + + self.cgen.stmt( \ + "%s = %s%s->dupArray(%s, %s)" % \ + (accessRhs, + self.makeCastExpr(vulkanType.getForNonConstAccess()), + self.poolVarName, + accessLhs, + bytesExpr)) + + self.cgen.endIf() + + def onValue(self, vulkanType): + if self.skipValues: + return + + accessLhs = self.exprAccessorValueLhs(vulkanType) + accessRhs = self.exprAccessorValueRhs(vulkanType) + + self.cgen.stmt("%s = %s" % (accessRhs, accessLhs)) + +class VulkanDeepcopy(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.deepcopyPrefix = "deepcopy_" + self.deepcopyVars = ["from", "to"] + self.deepcopyAllocatorVarName = "alloc" + self.deepcopyAllocatorParam = \ + makeVulkanTypeSimple(False, "Allocator", 1, + self.deepcopyAllocatorVarName) + self.deepcopyRootVarName = "rootType" + self.deepcopyRootParam = \ + makeVulkanTypeSimple(False, "VkStructureType", + 0, self.deepcopyRootVarName) + self.voidType = makeVulkanTypeSimple(False, "void", 0) + + self.deepcopyCodegen = \ + DeepcopyCodegen( + None, + self.deepcopyVars, + self.deepcopyAllocatorVarName, + self.deepcopyRootVarName, + self.deepcopyPrefix, + skipValues=True) + + self.knownDefs = {} + + self.extensionDeepcopyPrototype = \ + VulkanAPI(self.deepcopyPrefix + "extension_struct", + self.voidType, + [self.deepcopyAllocatorParam, + self.deepcopyRootParam, + STRUCT_EXTENSION_PARAM, + STRUCT_EXTENSION_PARAM_FOR_WRITE]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.codegen.makeFuncDecl( + self.extensionDeepcopyPrototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + self.module.appendHeader( + self.codegen.makeFuncAlias(self.deepcopyPrefix + name, + self.deepcopyPrefix + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + typeFromName = \ + lambda varname: \ + makeVulkanTypeSimple(varname == "from", name, 1, varname) + + deepcopyParams = \ + [self.deepcopyAllocatorParam, self.deepcopyRootParam] + \ + list(map(typeFromName, self.deepcopyVars)) + + deepcopyPrototype = \ + VulkanAPI(self.deepcopyPrefix + name, + self.voidType, + deepcopyParams) + + def structDeepcopyDef(cgen): + self.deepcopyCodegen.cgen = cgen + canSimplyAssign = True + for member in structInfo.members: + if not member.isSimpleValueType(self.typeInfo): + canSimplyAssign = False + + cgen.stmt("(void)%s" % self.deepcopyAllocatorVarName) + cgen.stmt("(void)%s" % self.deepcopyRootVarName) + cgen.stmt("*to = *from") + if canSimplyAssign: + pass + else: + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, + self.deepcopyCodegen) + + self.module.appendHeader( + self.codegen.makeFuncDecl(deepcopyPrototype)) + self.module.appendImpl( + self.codegen.makeFuncImpl(deepcopyPrototype, structDeepcopyDef)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def deepcopyDstExpr(cgen, typeName): + return cgen.makeReinterpretCast( \ + STRUCT_EXTENSION_PARAM_FOR_WRITE.paramName, + typeName, const=False) + + def forEachExtensionDeepcopy(ext, castedAccess, cgen): + cgen.funcCall(None, self.deepcopyPrefix + ext.name, + [self.deepcopyAllocatorVarName, + self.deepcopyRootVarName, + castedAccess, deepcopyDstExpr(cgen, ext.name)]) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionDeepcopyPrototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + self.voidType, + STRUCT_EXTENSION_PARAM, + forEachExtensionDeepcopy, + rootTypeVar=self.deepcopyRootParam))) diff --git a/src/gfxstream/codegen/scripts/cereal/dispatch.py b/src/gfxstream/codegen/scripts/cereal/dispatch.py new file mode 100644 index 00000000000..031f52ed69e --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/dispatch.py @@ -0,0 +1,501 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType + +from .wrapperdefs import VulkanWrapperGenerator + +# No real good way to automatically infer the most important Vulkan API +# functions as it relates to which getProcAddress function to use, plus +# we might want to control which function to use depending on our +# performance needs. + +# This is based on the minimum set of functions needed to be directly +# queried with dlsym and not returning null. +getProcAddrFuncs = [ + "vkGetInstanceProcAddr", + "vkDestroyInstance", + "vkEnumeratePhysicalDevices", + "vkGetPhysicalDeviceFeatures", + "vkGetPhysicalDeviceFormatProperties", + "vkGetPhysicalDeviceImageFormatProperties", + "vkGetPhysicalDeviceProperties", + "vkGetPhysicalDeviceQueueFamilyProperties", + "vkGetPhysicalDeviceMemoryProperties", + "vkCreateDevice", + "vkDestroyDevice", + "vkEnumerateDeviceExtensionProperties", + "vkEnumerateDeviceLayerProperties", +] + +# Some methods can only be found using dlsym() while we cannot get the function +# address using vkGetInstProcAddr() or vkGetDeviceProcAddr(). These function +# pointers should only be initialized when setting up the dispatch from system +# loader. +getProcAddrOnlyFuncs = [ + "vkGetMTLDeviceMVK", + "vkSetMTLTextureMVK", + "vkGetMTLTextureMVK", + "vkGetMTLBufferMVK", + "vkUseIOSurfaceMVK", + "vkGetIOSurfaceMVK", +] + +getInstanceProcAddrNoInstanceFuncs = [ + "vkCreateInstance", + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateInstanceLayerProperties", +] + +getInstanceProcAddrFuncs = [ + "vkGetDeviceProcAddr", + "vkCreateSwapchainKHR", + "vkDestroySwapchainKHR", + "vkGetSwapchainImagesKHR", + "vkAcquireNextImageKHR", + "vkQueuePresentKHR", + "vkCreateMacOSSurfaceMVK", + "vkCreateWin32SurfaceKHR", + "vkGetPhysicalDeviceWin32PresentationSupportKHR", + "vkCreateXlibSurfaceKHR", + "vkGetPhysicalDeviceXlibPresentationSupportKHR", + "vkCreateXcbSurfaceKHR", + "vkGetPhysicalDeviceXcbPresentationSupportKHR", + "vkGetPhysicalDeviceSparseImageFormatProperties", + "vkEnumerateInstanceVersion", + "vkEnumeratePhysicalDeviceGroups", + "vkGetPhysicalDeviceFeatures2", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceFormatProperties2", + "vkGetPhysicalDeviceImageFormatProperties2", + "vkGetPhysicalDeviceQueueFamilyProperties2", + "vkGetPhysicalDeviceMemoryProperties2", + "vkGetPhysicalDeviceSparseImageFormatProperties2", + "vkGetPhysicalDeviceExternalBufferProperties", + "vkGetPhysicalDeviceExternalFenceProperties", + "vkGetPhysicalDeviceExternalSemaphoreProperties", +] + +# Implicitly, everything else is going to be obtained +# with vkGetDeviceProcAddr, +# unless it has instance in the arg. + +def isGetProcAddressAPI(vulkanApi): + return vulkanApi.name in getProcAddrFuncs + +def isGetProcAddressOnlyAPI(vulkanApi): + return vulkanApi.name in getProcAddrOnlyFuncs + +def isGetInstanceProcAddressNoInstanceAPI(vulkanApi): + return vulkanApi.name in getInstanceProcAddrNoInstanceFuncs + +def isGetInstanceProcAddressAPI(vulkanApi): + if vulkanApi.name in getInstanceProcAddrFuncs: + return True + + if vulkanApi.parameters[0].typeName == "VkInstance": + return True + + return False + +def isGetDeviceProcAddressAPI(vulkanApi): + if isGetProcAddressAPI(vulkanApi): + return False + + if isGetProcAddressOnlyAPI(vulkanApi): + return False + + if isGetInstanceProcAddressAPI(vulkanApi): + return False + + return True + +def inferProcAddressFuncType(vulkanApi): + if isGetProcAddressAPI(vulkanApi): + return "global" + if isGetProcAddressOnlyAPI(vulkanApi): + return "global-only" + if isGetInstanceProcAddressNoInstanceAPI(vulkanApi): + return "global-instance" + if isGetInstanceProcAddressAPI(vulkanApi): + return "instance" + return "device" + +# VulkanDispatch defines a struct, VulkanDispatch, +# that is populated by function pointers from the Vulkan +# loader. No attempt is made to do something different +# for instance vs device functions. +class VulkanDispatch(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.apisToGet = {} + + self.cgenHeader = CodeGen() + self.cgenImpl = CodeGen() + self.typeInfo = typeInfo + + self.currentFeature = "" + self.featureForCodegen = "" + + def onBegin(self): + + # The first way is to use just the loader to get symbols. This doesn't + # necessarily work with extensions because at that point the dispatch + # table needs to be specific to a particular Vulkan instance or device. + + self.cgenHeader.line(""" +void init_vulkan_dispatch_from_system_loader( + DlOpenFunc dlOpenFunc, + DlSymFunc dlSymFunc, + VulkanDispatch* dispatch_out); +""") + + # The second way is to initialize the table from a given Vulkan + # instance or device. Provided the instance or device was created with + # the right extensions, we can obtain function pointers to extension + # functions this way. + + self.cgenHeader.line(""" +void init_vulkan_dispatch_from_instance( + VulkanDispatch* vk, + VkInstance instance, + VulkanDispatch* dispatch_out); +""") + self.cgenHeader.line(""" +void init_vulkan_dispatch_from_device( + VulkanDispatch* vk, + VkDevice device, + VulkanDispatch* dispatch_out); +""") + + # After populating a VulkanDispatch with the above methods, + # it can be useful to check whether the Vulkan 1.0 or 1.1 methods + # are all there. + def emit_feature_check_decl(cgen, tag, featureToCheck): + cgen.line(""" +bool vulkan_dispatch_check_%s_%s( + const VulkanDispatch* vk); +""" % (tag, featureToCheck)) + + emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_0") + emit_feature_check_decl(self.cgenHeader, "instance", "VK_VERSION_1_1") + emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_0") + emit_feature_check_decl(self.cgenHeader, "device", "VK_VERSION_1_1") + + self.cgenHeader.line("struct VulkanDispatch {") + self.module.appendHeader(self.cgenHeader.swapCode()) + + def syncFeatureQuiet(self, cgen, feature): + if self.featureForCodegen != feature: + if feature == "": + self.featureForCodegen = feature + return + + self.featureForCodegen = feature + + def syncFeature(self, cgen, feature): + if self.featureForCodegen != feature: + if feature == "": + cgen.leftline("#endif") + self.featureForCodegen = feature + return + + if self.featureForCodegen != "": + cgen.leftline("#endif") + + cgen.leftline("#ifdef %s" % feature) + self.featureForCodegen = feature + + def makeDlsymCall(self, cgen, apiname, typedecl): + cgen.stmt( \ + "out->%s = (%s)dlSymFunc(lib, \"%s\")" % \ + (apiname, typedecl, apiname)) + + def makeGetInstanceProcAddrCall(self, cgen, dispatch, instance, apiname, typedecl): + cgen.stmt( \ + "out->%s = (%s)%s->vkGetInstanceProcAddr(%s, \"%s\")" % \ + (apiname, typedecl, dispatch, instance, apiname)) + + def makeGetDeviceProcAddrCall(self, cgen, dispatch, device, apiname, typedecl): + cgen.stmt( \ + "out->%s = (%s)%s->vkGetDeviceProcAddr(%s, \"%s\")" % \ + (apiname, typedecl, dispatch, device, apiname)) + + def onEnd(self): + self.cgenHeader.line("};") + self.module.appendHeader(self.cgenHeader.swapCode()) + + # Getting dispatch tables from the loader + self.cgenImpl.line(""" +void init_vulkan_dispatch_from_system_loader( + DlOpenFunc dlOpenFunc, + DlSymFunc dlSymFunc, + VulkanDispatch* out)""") + + self.cgenImpl.beginBlock() + + self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") + + self.cgenImpl.stmt("void* lib = dlOpenFunc()") + self.cgenImpl.stmt("if (!lib) return") + + apis = \ + self.apisToGet["global"] + \ + self.apisToGet["global-instance"] + \ + self.apisToGet["instance"] + \ + self.apisToGet["device"] + + if "global-only" in self.apisToGet: + apis = apis + self.apisToGet["global-only"] + + for vulkanApi, typeDecl, feature in apis: + self.syncFeature(self.cgenImpl, feature) + self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) + + self.syncFeature(self.cgenImpl, "") + self.cgenImpl.endBlock() + + # Getting instance dispatch tables + self.cgenImpl.line(""" +void init_vulkan_dispatch_from_instance( + VulkanDispatch* vk, + VkInstance instance, + VulkanDispatch* out)""") + + self.cgenImpl.beginBlock() + + self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") + + apis = \ + self.apisToGet["global"] + \ + self.apisToGet["global-instance"] + \ + self.apisToGet["instance"] + \ + self.apisToGet["device"] + + for vulkanApi, typeDecl, feature in apis: + self.syncFeature(self.cgenImpl, feature) + self.makeGetInstanceProcAddrCall( + self.cgenImpl, "vk", "instance", vulkanApi.name, typeDecl) + + self.syncFeature(self.cgenImpl, "") + self.cgenImpl.endBlock() + + # Getting device dispatch tables + self.cgenImpl.line(""" +void init_vulkan_dispatch_from_device( + VulkanDispatch* vk, + VkDevice device, + VulkanDispatch* out)""") + + self.cgenImpl.beginBlock() + + self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))") + + apis = \ + self.apisToGet["global"] + \ + self.apisToGet["global-instance"] + \ + self.apisToGet["instance"] + \ + self.apisToGet["device"] + + for vulkanApi, typeDecl, feature in apis: + self.syncFeature(self.cgenImpl, feature) + self.makeGetDeviceProcAddrCall( + self.cgenImpl, "vk", "device", vulkanApi.name, typeDecl) + + self.syncFeature(self.cgenImpl, "") + self.cgenImpl.endBlock() + + # Check Vulkan 1.0 / 1.1 functions + + def emit_check_impl(cgen, dispatchVar, feature, featureToCheck, apiName): + if feature == featureToCheck: + cgen.beginIf("!%s->%s" % (dispatchVar, apiName)) + cgen.stmt("fprintf(stderr, \"%s check failed: %s not found\\n\")" % (featureToCheck, apiName)) + cgen.stmt("good = false") + cgen.endIf() + + def emit_feature_check_impl(context, cgen, tag, featureToCheck, apis): + cgen.line(""" +bool vulkan_dispatch_check_%s_%s( + const VulkanDispatch* vk) +""" % (tag, featureToCheck)) + + cgen.beginBlock() + + cgen.stmt("bool good = true") + + for vulkanApi, typeDecl, feature in apis: + context.syncFeatureQuiet(self.cgenImpl, feature) + emit_check_impl(cgen, "vk", feature, featureToCheck, vulkanApi.name) + + context.syncFeatureQuiet(self.cgenImpl, "") + + cgen.stmt("return good") + cgen.endBlock() + + instanceApis = self.apisToGet["global-instance"] + self.apisToGet["instance"] + + emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_0", instanceApis) + emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_VERSION_1_1", instanceApis) + emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_0", self.apisToGet["device"]) + emit_feature_check_impl(self, self.cgenImpl, "device", "VK_VERSION_1_1", self.apisToGet["device"]) + + self.module.appendImpl(self.cgenImpl.swapCode()) + + def onBeginFeature(self, featureName, featureType): + self.currentFeature = featureName + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + vulkanApi = self.typeInfo.apis[name] + + typeDecl = "PFN_%s" % name + + procAddressType = inferProcAddressFuncType(vulkanApi) + + self.cgenHeader.stmt("%s %s" % (typeDecl, name)); + self.module.appendHeader(self.cgenHeader.swapCode()) + + current = self.apisToGet.get(procAddressType, []) + if current == []: + self.apisToGet[procAddressType] = current + current.append((vulkanApi, typeDecl, self.currentFeature)) + +# VulkanDispatchFast allows one to get the optimal function pointers +# for a given Vulkan API call, in order to improve performance. +# +# We can optionally query VkDevices to get function pointers that are +# closer to the ICD and have fewer levels of indirection from the loader +# to get there. +# See +# https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md +# for more info. +# +# This requires the calling C++ code to provide functions to +# generate the desired instances and devices, otherwise we won't know +# which instance or device to pass to vkGet(Instance|Device)ProcAddr, +# so it does push more complexity to the user. +class VulkanDispatchFast(VulkanDispatch): + + def __init__(self, module, typeInfo): + VulkanDispatch.__init__(self, module, typeInfo) + + def onBegin(self): + self.cgenHeader.line(""" +void init_vulkan_dispatch_from_system_loader( + DlOpenFunc dlOpenFunc, + DlSymFunc dlSymFunc, + InstanceGetter instanceGetter, + DeviceGetter deviceGetter, + VulkanDispatch* dispatch_out); +""") + + self.cgenHeader.line("struct VulkanDispatch {") + self.cgenHeader.line("VkInstance instance;") + self.cgenHeader.line("VkPhysicalDevice physicalDevice;") + self.cgenHeader.line("uint32_t physicalDeviceQueueFamilyInfoCount;") + self.cgenHeader.line("VkQueueFamilyProperties* physicalDeviceQueueFamilyInfos;") + self.cgenHeader.line("VkDevice device;") + self.cgenHeader.line("bool presentCapable;") + self.module.appendHeader(self.cgenHeader.swapCode()) + + def makeGetProcAddr(self, cgen, dispatchLevel, dispatch, apiname, typedecl): + if dispatchLevel == "instance": + funcname = "vkGetInstanceProcAddr" + elif dispatchLevel == "device": + funcname = "vkGetDeviceProcAddr" + else: + raise + + cgen.stmt( \ + "out->%s = (%s)out->%s(%s, \"%s\")" % \ + (apiname, typedecl, funcname, dispatch, apiname)) + + def onEnd(self): + self.cgenHeader.line("};") + self.module.appendHeader(self.cgenHeader.swapCode()) + + self.cgenImpl.line(""" +void init_vulkan_dispatch_from_system_loader( + DlOpenFunc dlOpenFunc, + DlSymFunc dlSymFunc, + InstanceGetter instanceGetter, + DeviceGetter deviceGetter, + VulkanDispatch* out)""") + + self.cgenImpl.beginBlock() + + self.cgenImpl.stmt("out->instance = nullptr") + self.cgenImpl.stmt("out->physicalDevice = nullptr") + self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfoCount = 0") + self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = nullptr") + self.cgenImpl.stmt("out->device = nullptr") + self.cgenImpl.stmt("out->presentCapable = false") + + self.cgenImpl.stmt("void* lib = dlOpenFunc()") + self.cgenImpl.stmt("if (!lib) return") + + for vulkanApi, typeDecl, feature in self.apisToGet["global"]: + self.syncFeature(self.cgenImpl, feature) + self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl) + + self.syncFeature(self.cgenImpl, "") + self.cgenImpl.stmt("if (!out->vkGetInstanceProcAddr) return") + + for vulkanApi, typeDecl, feature in self.apisToGet["global-instance"]: + self.syncFeature(self.cgenImpl, feature) + self.makeGetProcAddr( \ + self.cgenImpl, "instance", "nullptr", vulkanApi.name, typeDecl); + + self.syncFeature(self.cgenImpl, "") + self.cgenImpl.stmt("if (!instanceGetter(out, &out->instance)) return") + + for vulkanApi, typeDecl, feature in self.apisToGet["instance"]: + self.syncFeature(self.cgenImpl, feature) + self.makeGetProcAddr( \ + self.cgenImpl, "instance", "out->instance", vulkanApi.name, typeDecl); + + self.syncFeature(self.cgenImpl, "") + + self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, nullptr, &out->device, &out->presentCapable)) return") + self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = (VkQueueFamilyProperties*)malloc(out->physicalDeviceQueueFamilyInfoCount * sizeof(VkQueueFamilyProperties))"); + self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, out->physicalDeviceQueueFamilyInfos, &out->device, &out->presentCapable)) return") + + for vulkanApi, typeDecl, feature in self.apisToGet["device"]: + self.syncFeature(self.cgenImpl, feature) + self.makeGetProcAddr( \ + self.cgenImpl, "device", "out->device", vulkanApi.name, typeDecl); + + self.syncFeature(self.cgenImpl, "") + + self.cgenImpl.endBlock() + + self.module.appendImpl(self.cgenImpl.swapCode()) + + def onBeginFeature(self, featureName, featureType): + VulkanDispatch.onBeginFeature(self, featureName, featureType); + + def onGenType(self, typeXml, name, alias): + VulkanDispatch.onGenType(self, typeXml, name, alias); + + def onGenCmd(self, cmdinfo, name, alias): + VulkanDispatch.onGenCmd(self, cmdinfo, name, alias); diff --git a/src/gfxstream/codegen/scripts/cereal/encoder.py b/src/gfxstream/codegen/scripts/cereal/encoder.py new file mode 100644 index 00000000000..ff62215d16e --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/encoder.py @@ -0,0 +1,723 @@ +import copy + +from .common.codegen import CodeGen, VulkanWrapperGenerator +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType + +from .marshaling import VulkanMarshalingCodegen +from .reservedmarshaling import VulkanReservedMarshalingCodegen +from .counting import VulkanCountingCodegen +from .handlemap import HandleMapCodegen +from .deepcopy import DeepcopyCodegen +from .transform import TransformCodegen, genTransformsForVulkanType + +from .wrapperdefs import API_PREFIX_RESERVEDMARSHAL +from .wrapperdefs import API_PREFIX_MARSHAL +from .wrapperdefs import API_PREFIX_UNMARSHAL +from .wrapperdefs import ROOT_TYPE_DEFAULT_VALUE +from .wrapperdefs import VULKAN_STREAM_TYPE_GUEST + +encoder_decl_preamble = """ + +class VkEncoder { +public: + VkEncoder(gfxstream::guest::IOStream* stream, gfxstream::guest::HealthMonitor<>* healthMonitor = nullptr); + ~VkEncoder(); + +#include "VkEncoder.h.inl" +""" + +encoder_decl_postamble = """ +private: + class Impl; + std::unique_ptr mImpl; + gfxstream::guest::HealthMonitor<>* mHealthMonitor; +}; +""" + +encoder_impl_preamble =""" + +using namespace gfxstream::vk; + +using gfxstream::guest::AutoLock; +using gfxstream::guest::Lock; +using gfxstream::guest::BumpPool; + +#include "VkEncoder.cpp.inl" + +#define VALIDATE_RET(retType, success, validate) \\ + retType goldfish_vk_validateResult = validate; \\ + if (goldfish_vk_validateResult != success) return goldfish_vk_validateResult; \\ + +#define VALIDATE_VOID(validate) \\ + VkResult goldfish_vk_validateResult = validate; \\ + if (goldfish_vk_validateResult != VK_SUCCESS) return; \\ + +""" + +STREAM = "stream" +RESOURCES = "sResourceTracker" +POOL = "pool" + +ENCODER_PREVALIDATED_APIS = [ + "vkFlushMappedMemoryRanges", + "vkInvalidateMappedMemoryRanges", +] + +ENCODER_CUSTOM_RESOURCE_PREPROCESS = [ + "vkMapMemoryIntoAddressSpaceGOOGLE", + "vkDestroyDevice", +] + +ENCODER_CUSTOM_RESOURCE_POSTPROCESS = [ + "vkCreateInstance", + "vkCreateDevice", + "vkMapMemoryIntoAddressSpaceGOOGLE", + "vkGetPhysicalDeviceFeatures2", + "vkGetPhysicalDeviceFeatures2KHR", + "vkGetPhysicalDeviceProperties", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", + "vkCreateDescriptorUpdateTemplate", + "vkCreateDescriptorUpdateTemplateKHR", + "vkGetPhysicalDeviceExternalSemaphoreProperties", + "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR", + "vkGetDeviceQueue", + "vkGetDeviceQueue2", +] + +ENCODER_EXPLICIT_FLUSHED_APIS = [ + "vkEndCommandBufferAsyncGOOGLE", + "vkQueueSubmitAsyncGOOGLE", + "vkQueueBindSparseAsyncGOOGLE", + "vkQueueWaitIdleAsyncGOOGLE", + "vkQueueSignalReleaseImageANDROID", + "vkDestroyDevice", +] + +SUCCESS_RET_TYPES = { + "VkResult" : "VK_SUCCESS", + "void" : None, + # TODO: Put up success results for other return types here. +} + +ENCODER_THIS_PARAM = makeVulkanTypeSimple(False, "VkEncoder", 1, "this") + +# Common components of encoding a Vulkan API call +def make_event_handler_call( + handler_access, + api, + context_param, + input_result_param, + cgen, + suffix=""): + extraParams = [context_param.paramName] + if input_result_param: + extraParams.append(input_result_param) + return cgen.makeCallExpr( \ + "%s->on_%s%s" % (handler_access, api.name, suffix), + extraParams + \ + [p.paramName for p in api.parameters[:-1]]) + +def emit_custom_pre_validate(typeInfo, api, cgen): + if api.name in ENCODER_PREVALIDATED_APIS: + callExpr = \ + make_event_handler_call( \ + "mImpl->validation()", api, + ENCODER_THIS_PARAM, + SUCCESS_RET_TYPES[api.getRetTypeExpr()], + cgen) + + if api.getRetTypeExpr() == "void": + cgen.stmt("VALIDATE_VOID(%s)" % callExpr) + else: + cgen.stmt("VALIDATE_RET(%s, %s, %s)" % \ + (api.getRetTypeExpr(), + SUCCESS_RET_TYPES[api.getRetTypeExpr()], + callExpr)) + +def emit_custom_resource_preprocess(typeInfo, api, cgen): + if api.name in ENCODER_CUSTOM_RESOURCE_PREPROCESS: + cgen.stmt( \ + make_event_handler_call( \ + "sResourceTracker", api, + ENCODER_THIS_PARAM, + SUCCESS_RET_TYPES[api.getRetTypeExpr()], + cgen, suffix="_pre")) + +def emit_custom_resource_postprocess(typeInfo, api, cgen): + if api.name in ENCODER_CUSTOM_RESOURCE_POSTPROCESS: + cgen.stmt(make_event_handler_call( \ + "sResourceTracker", + api, + ENCODER_THIS_PARAM, + api.getRetVarExpr(), + cgen)) + +def emit_count_marshal(typeInfo, param, cgen): + res = \ + iterateVulkanType( + typeInfo, param, + VulkanCountingCodegen( \ + cgen, "sFeatureBits", param.paramName, "countPtr", ROOT_TYPE_DEFAULT_VALUE, + "count_")) + if not res: + cgen.stmt("(void)%s" % param.paramName) + +def emit_marshal(typeInfo, param, cgen): + forOutput = param.isHandleType() and ("out" in param.inout) + if forOutput: + cgen.stmt("/* is handle, possibly out */") + + res = \ + iterateVulkanType( + typeInfo, param, + VulkanReservedMarshalingCodegen( \ + cgen, "guest", STREAM, ROOT_TYPE_DEFAULT_VALUE, param.paramName, "streamPtrPtr", + API_PREFIX_RESERVEDMARSHAL, + "" if forOutput else "get_host_u64_", + direction="write")) + if not res: + cgen.stmt("(void)%s" % param.paramName) + + if forOutput: + cgen.stmt("/* is handle, possibly out */") + +def emit_unmarshal(typeInfo, param, cgen): + iterateVulkanType( + typeInfo, param, + VulkanMarshalingCodegen( \ + cgen, STREAM, ROOT_TYPE_DEFAULT_VALUE, param.paramName, + API_PREFIX_UNMARSHAL, direction="read")) + +def emit_deepcopy(typeInfo, param, cgen): + res = \ + iterateVulkanType(typeInfo, param, DeepcopyCodegen( + cgen, [param.paramName, "local_" + param.paramName], "pool", ROOT_TYPE_DEFAULT_VALUE, "deepcopy_")) + if not res: + cgen.stmt("(void)%s" % param.paramName) + +def emit_transform(typeInfo, param, cgen, variant="tohost"): + res = \ + iterateVulkanType(typeInfo, param, TransformCodegen( \ + cgen, param.paramName, "sResourceTracker", "transform_%s_" % variant, variant)) + if not res: + cgen.stmt("(void)%s" % param.paramName) + +def emit_handlemap_create(typeInfo, param, cgen): + iterateVulkanType(typeInfo, param, HandleMapCodegen( + cgen, None, "sResourceTracker", "handlemap_", + lambda vtype: typeInfo.isHandleType(vtype.typeName) + )) + +def custom_encoder_args(api): + params = ["this"] + if api.getRetVarExpr() is not None: + params.append(api.getRetVarExpr()) + return params + +def emit_handlemap_destroy(typeInfo, param, cgen): + iterateVulkanType(typeInfo, param, HandleMapCodegen( + cgen, None, "sResourceTracker->destroyMapping()", "handlemap_", + lambda vtype: typeInfo.isHandleType(vtype.typeName) + )) + +class EncodingParameters(object): + def __init__(self, api): + self.localCopied = [] + self.toWrite = [] + self.toRead = [] + self.toCreate = [] + self.toDestroy = [] + + for param in api.parameters: + param.action = None + param.inout = "in" + + if param.paramName == "doLock": + continue + + if param.possiblyOutput(): + param.inout += "out" + self.toWrite.append(param) + self.toRead.append(param) + if param.isCreatedBy(api): + self.toCreate.append(param) + param.action = "create" + else: + + if param.paramName == "doLock": + continue + + if param.isDestroyedBy(api): + self.toDestroy.append(param) + param.action = "destroy" + localCopyParam = \ + param.getForNonConstAccess().withModifiedName( \ + "local_" + param.paramName) + self.localCopied.append((param, localCopyParam)) + self.toWrite.append(localCopyParam) + +def emit_parameter_encode_preamble_write(typeInfo, api, cgen): + emit_custom_pre_validate(typeInfo, api, cgen); + emit_custom_resource_preprocess(typeInfo, api, cgen); + + cgen.stmt("auto %s = mImpl->stream()" % STREAM) + cgen.stmt("auto %s = mImpl->pool()" % POOL) + # cgen.stmt("%s->setHandleMapping(%s->unwrapMapping())" % (STREAM, RESOURCES)) + + encodingParams = EncodingParameters(api) + for (_, localCopyParam) in encodingParams.localCopied: + cgen.stmt(cgen.makeRichCTypeDecl(localCopyParam)) + +def emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen, customUnwrap=None): + encodingParams = EncodingParameters(api) + + for (origParam, localCopyParam) in encodingParams.localCopied: + shouldCustomCopy = \ + customUnwrap and \ + origParam.paramName in customUnwrap and \ + "copyOp" in customUnwrap[origParam.paramName] + + shouldCustomMap = \ + customUnwrap and \ + origParam.paramName in customUnwrap and \ + "mapOp" in customUnwrap[origParam.paramName] + + if shouldCustomCopy: + customUnwrap[origParam.paramName]["copyOp"](cgen, origParam, localCopyParam) + else: + # if this is a pointer type and we don't do custom copy nor unwrap, + # and the transform doesn't end up doing anything, + # don't deepcopy, just cast it. + + avoidDeepcopy = False + + if origParam.pointerIndirectionLevels > 0: + testCgen = CodeGen() + genTransformsForVulkanType("sResourceTracker", origParam, lambda p: testCgen.generalAccess(p, parentVarName = None, asPtr = True), lambda p: testCgen.generalLengthAccess(p, parentVarName = None), testCgen) + emit_transform(typeInfo, origParam, testCgen, variant="tohost") + if "" == testCgen.swapCode(): + avoidDeepcopy = True + if avoidDeepcopy: + cgen.line("// Avoiding deepcopy for %s" % origParam.paramName) + cgen.stmt("%s = (%s%s)%s" % (localCopyParam.paramName, localCopyParam.typeName, "*" * origParam.pointerIndirectionLevels, origParam.paramName)) + else: + emit_deepcopy(typeInfo, origParam, cgen) + + for (origParam, localCopyParam) in encodingParams.localCopied: + shouldCustomMap = \ + customUnwrap and \ + origParam.paramName in customUnwrap and \ + "mapOp" in customUnwrap[origParam.paramName] + + if shouldCustomMap: + customUnwrap[origParam.paramName]["mapOp"](cgen, origParam, localCopyParam) + else: + if localCopyParam.typeName == "VkAllocationCallbacks": + cgen.stmt("%s = nullptr" % localCopyParam.paramName) + + apiForTransform = \ + api.withCustomParameters( \ + map(lambda p: p[1], \ + encodingParams.localCopied)) + + # Apply transforms if applicable. + # Apply transform to API itself: + genTransformsForVulkanType( + "sResourceTracker", + apiForTransform, + lambda p: cgen.generalAccess(p, parentVarName = None, asPtr = True), + lambda p: cgen.generalLengthAccess(p, parentVarName = None), + cgen) + + # For all local copied parameters, run the transforms + for localParam in apiForTransform.parameters: + if "doLock" in localParam.paramName: + continue + emit_transform(typeInfo, localParam, cgen, variant="tohost") + + cgen.stmt("size_t count = 0") + cgen.stmt("size_t* countPtr = &count") + cgen.beginBlock() + + # Use counting stream to calculate the packet size. + for p in encodingParams.toWrite: + emit_count_marshal(typeInfo, p, cgen) + + cgen.endBlock() + +def is_cmdbuf_dispatch(api): + return "VkCommandBuffer" == api.parameters[0].typeName + +def emit_parameter_encode_write_packet_info(typeInfo, api, cgen): + # Seqno and skipping dispatch serialize are for use with VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT + doSeqno = True + doDispatchSerialize = True + + if is_cmdbuf_dispatch(api): + doSeqno = False + doDispatchSerialize = False + + if doSeqno: + cgen.stmt("uint32_t packetSize_%s = 4 + 4 + (queueSubmitWithCommandsEnabled ? 4 : 0) + count" % (api.name)) + else: + cgen.stmt("uint32_t packetSize_%s = 4 + 4 + count" % (api.name)) + cgen.stmt("healthMonitorAnnotation_packetSize = std::make_optional(packetSize_%s)" % (api.name)) + + if not doDispatchSerialize: + cgen.stmt("if (queueSubmitWithCommandsEnabled) packetSize_%s -= 8" % api.name) + + cgen.stmt("uint8_t* streamPtr = %s->reserve(packetSize_%s)" % (STREAM, api.name)) + cgen.stmt("uint8_t* packetBeginPtr = streamPtr") + cgen.stmt("uint8_t** streamPtrPtr = &streamPtr") + cgen.stmt("uint32_t opcode_%s = OP_%s" % (api.name, api.name)) + + if doSeqno: + cgen.stmt("uint32_t seqno; if (queueSubmitWithCommandsEnabled) seqno = ResourceTracker::nextSeqno()") + cgen.stmt("healthMonitorAnnotation_seqno = std::make_optional(seqno)") + + cgen.stmt("memcpy(streamPtr, &opcode_%s, sizeof(uint32_t)); streamPtr += sizeof(uint32_t)" % api.name) + cgen.stmt("memcpy(streamPtr, &packetSize_%s, sizeof(uint32_t)); streamPtr += sizeof(uint32_t)" % api.name) + + if doSeqno: + cgen.line("if (queueSubmitWithCommandsEnabled) { memcpy(streamPtr, &seqno, sizeof(uint32_t)); streamPtr += sizeof(uint32_t); }") + +def emit_parameter_encode_do_parameter_write(typeInfo, api, cgen): + encodingParams = EncodingParameters(api) + + dispatchDone = False + + for p in encodingParams.toWrite: + if is_cmdbuf_dispatch(api) and not dispatchDone: + cgen.beginIf("!queueSubmitWithCommandsEnabled") + emit_marshal(typeInfo, p, cgen) + cgen.endIf() + else: + emit_marshal(typeInfo, p, cgen) + + dispatchDone = True + + cgen.beginIf("watchdog") + cgen.stmt("size_t watchdogBufSize = std::min(static_cast(packetSize_%s), kWatchdogBufferMax)" % (api.name)) + cgen.stmt("healthMonitorAnnotation_packetContents.resize(watchdogBufSize)") + cgen.stmt("memcpy(&healthMonitorAnnotation_packetContents[0], packetBeginPtr, watchdogBufSize)") + cgen.endIf() + +def emit_parameter_encode_read(typeInfo, api, cgen): + encodingParams = EncodingParameters(api) + + for p in encodingParams.toRead: + if p.action == "create": + cgen.stmt( + "%s->setHandleMapping(%s->createMapping())" % \ + (STREAM, RESOURCES)) + emit_unmarshal(typeInfo, p, cgen) + if p.action == "create": + cgen.stmt( + "%s->unsetHandleMapping()" % STREAM) + emit_transform(typeInfo, p, cgen, variant="fromhost") + +def emit_post(typeInfo, api, cgen): + encodingParams = EncodingParameters(api) + + emit_custom_resource_postprocess(typeInfo, api, cgen) + + for p in encodingParams.toDestroy: + emit_handlemap_destroy(typeInfo, p, cgen) + + doSeqno = True + if is_cmdbuf_dispatch(api): + doSeqno = False + + retType = api.getRetTypeExpr() + + if api.name in ENCODER_EXPLICIT_FLUSHED_APIS: + cgen.stmt("stream->flush()"); + return + + if doSeqno: + if retType == "void": + encodingParams = EncodingParameters(api) + if 0 == len(encodingParams.toRead): + cgen.stmt("stream->flush()"); + +def emit_pool_free(cgen): + cgen.stmt("++encodeCount;") + cgen.beginIf("0 == encodeCount % POOL_CLEAR_INTERVAL") + cgen.stmt("pool->freeAll()") + cgen.stmt("%s->clearPool()" % STREAM) + cgen.endIf() + +def emit_return_unmarshal(typeInfo, api, cgen): + + retType = api.getRetTypeExpr() + + if retType == "void": + return + + retVar = api.getRetVarExpr() + cgen.stmt("%s %s = (%s)0" % (retType, retVar, retType)) + cgen.stmt("%s->read(&%s, %s)" % \ + (STREAM, retVar, cgen.sizeofExpr(api.retType))) + +def emit_return(typeInfo, api, cgen): + if api.getRetTypeExpr() == "void": + return + + retVar = api.getRetVarExpr() + cgen.stmt("return %s" % retVar) + +def emit_lock(cgen): + cgen.stmt("(void)doLock"); + cgen.stmt("bool queueSubmitWithCommandsEnabled = sFeatureBits & VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT") + cgen.stmt("if (!queueSubmitWithCommandsEnabled && doLock) this->lock()") + +def emit_unlock(cgen): + cgen.stmt("if (!queueSubmitWithCommandsEnabled && doLock) this->unlock()") + +def emit_debug_log(typeInfo, api, cgen): + logFormat = [] + logVargs = [] + for param in api.parameters: + if param.paramName == "doLock": + continue + + paramFormatSpecifier = param.getPrintFormatSpecifier() + if not paramFormatSpecifier: + continue + + logFormat.append(param.paramName + ":" + paramFormatSpecifier) + logVargs.append(param.paramName) + + logFormatStr = ", ".join(logFormat) + logVargsStr = ", ".join(logVargs) + + cgen.stmt("ENCODER_DEBUG_LOG(\"%s(%s)\", %s)" % (api.name, logFormatStr, logVargsStr)) + +def emit_health_watchdog(api, cgen): + cgen.stmt("std::optional healthMonitorAnnotation_seqno = std::nullopt") + cgen.stmt("std::optional healthMonitorAnnotation_packetSize = std::nullopt") + cgen.stmt("std::vector healthMonitorAnnotation_packetContents") + cgen.line(""" + auto watchdog = WATCHDOG_BUILDER(mHealthMonitor, \"%s in VkEncoder\") + .setOnHangCallback([&]() { + auto annotations = std::make_unique(); + if (healthMonitorAnnotation_seqno) { + annotations->insert({{"seqno", std::to_string(healthMonitorAnnotation_seqno.value())}}); + } + if (healthMonitorAnnotation_packetSize) { + annotations->insert({{"packetSize", std::to_string(healthMonitorAnnotation_packetSize.value())}}); + } + if (!healthMonitorAnnotation_packetContents.empty()) { + annotations->insert( + {{"packetContents", getPacketContents( + &healthMonitorAnnotation_packetContents[0], healthMonitorAnnotation_packetContents.size())}}); + } + return std::move(annotations); + }) + .build(); + """% (api.name) + ) + +def emit_default_encoding(typeInfo, api, cgen): + emit_debug_log(typeInfo, api, cgen) + emit_lock(cgen) + emit_parameter_encode_preamble_write(typeInfo, api, cgen) + emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen) + emit_parameter_encode_write_packet_info(typeInfo, api, cgen) + emit_parameter_encode_do_parameter_write(typeInfo, api, cgen) + emit_parameter_encode_read(typeInfo, api, cgen) + emit_return_unmarshal(typeInfo, api, cgen) + emit_post(typeInfo, api, cgen) + emit_pool_free(cgen) + emit_unlock(cgen) + emit_return(typeInfo, api, cgen) + +## Custom encoding definitions################################################## + +def emit_only_goldfish_custom(typeInfo, api, cgen): + emit_lock(cgen) + cgen.vkApiCall( \ + api, + customPrefix="sResourceTracker->on_", + customParameters=custom_encoder_args(api) + \ + [p.paramName for p in api.parameters[:-1]]) + emit_unlock(cgen) + emit_return(typeInfo, api, cgen) + +def emit_only_resource_event(typeInfo, api, cgen): + cgen.stmt("(void)doLock"); + input_result = None + retExpr = api.getRetVarExpr() + + if retExpr: + retType = api.getRetTypeExpr() + input_result = SUCCESS_RET_TYPES[retType] + cgen.stmt("%s %s = (%s)0" % (retType, retExpr, retType)) + + cgen.stmt( + (("%s = " % retExpr) if retExpr else "") + + make_event_handler_call( + "sResourceTracker", + api, + ENCODER_THIS_PARAM, + input_result, cgen)) + + if retExpr: + emit_return(typeInfo, api, cgen) + +def emit_with_custom_unwrap(custom): + def call(typeInfo, api, cgen): + emit_lock(cgen) + emit_parameter_encode_preamble_write(typeInfo, api, cgen) + emit_parameter_encode_copy_unwrap_count( + typeInfo, api, cgen, customUnwrap=custom) + emit_parameter_encode_write_packet_info(typeInfo, api, cgen) + emit_parameter_encode_do_parameter_write(typeInfo, api, cgen) + emit_parameter_encode_read(typeInfo, api, cgen) + emit_return_unmarshal(typeInfo, api, cgen) + emit_pool_free(cgen) + emit_unlock(cgen) + emit_return(typeInfo, api, cgen) + return call + +def encode_vkFlushMappedMemoryRanges(typeInfo, api, cgen): + emit_lock(cgen) + emit_parameter_encode_preamble_write(typeInfo, api, cgen) + emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen) + + def emit_flush_ranges(streamVar): + cgen.beginIf("!sResourceTracker->usingDirectMapping()") + cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i") + cgen.stmt("auto range = pMemoryRanges[i]") + cgen.stmt("auto memory = pMemoryRanges[i].memory") + cgen.stmt("auto size = pMemoryRanges[i].size") + cgen.stmt("auto offset = pMemoryRanges[i].offset") + cgen.stmt("uint64_t streamSize = 0") + cgen.stmt("if (!memory) { %s->write(&streamSize, sizeof(uint64_t)); continue; }" % streamVar) + cgen.stmt("auto hostPtr = sResourceTracker->getMappedPointer(memory)") + cgen.stmt("auto actualSize = size == VK_WHOLE_SIZE ? sResourceTracker->getMappedSize(memory) : size") + cgen.stmt("if (!hostPtr) { %s->write(&streamSize, sizeof(uint64_t)); continue; }" % streamVar) + cgen.stmt("streamSize = actualSize") + cgen.stmt("%s->write(&streamSize, sizeof(uint64_t))" % streamVar) + cgen.stmt("uint8_t* targetRange = hostPtr + offset") + cgen.stmt("%s->write(targetRange, actualSize)" % streamVar) + cgen.endFor() + cgen.endIf() + + emit_parameter_encode_write_packet_info(typeInfo, api, cgen) + emit_parameter_encode_do_parameter_write(typeInfo, api, cgen) + + emit_flush_ranges(STREAM) + + emit_parameter_encode_read(typeInfo, api, cgen) + emit_return_unmarshal(typeInfo, api, cgen) + emit_pool_free(cgen) + emit_unlock(cgen) + emit_return(typeInfo, api, cgen) + +def encode_vkInvalidateMappedMemoryRanges(typeInfo, api, cgen): + emit_lock(cgen) + emit_parameter_encode_preamble_write(typeInfo, api, cgen) + emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen) + emit_parameter_encode_write_packet_info(typeInfo, api, cgen) + emit_parameter_encode_do_parameter_write(typeInfo, api, cgen) + emit_parameter_encode_read(typeInfo, api, cgen) + emit_return_unmarshal(typeInfo, api, cgen) + + def emit_invalidate_ranges(streamVar): + cgen.beginIf("!sResourceTracker->usingDirectMapping()") + cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i") + cgen.stmt("auto range = pMemoryRanges[i]") + cgen.stmt("auto memory = pMemoryRanges[i].memory") + cgen.stmt("auto size = pMemoryRanges[i].size") + cgen.stmt("auto offset = pMemoryRanges[i].offset") + cgen.stmt("uint64_t streamSize = 0") + cgen.stmt("if (!memory) { %s->read(&streamSize, sizeof(uint64_t)); continue; }" % streamVar) + cgen.stmt("auto hostPtr = sResourceTracker->getMappedPointer(memory)") + cgen.stmt("auto actualSize = size == VK_WHOLE_SIZE ? sResourceTracker->getMappedSize(memory) : size") + cgen.stmt("if (!hostPtr) { %s->read(&streamSize, sizeof(uint64_t)); continue; }" % streamVar) + cgen.stmt("streamSize = actualSize") + cgen.stmt("%s->read(&streamSize, sizeof(uint64_t))" % streamVar) + cgen.stmt("uint8_t* targetRange = hostPtr + offset") + cgen.stmt("%s->read(targetRange, actualSize)" % streamVar) + cgen.endFor() + cgen.endIf() + + emit_invalidate_ranges(STREAM) + emit_pool_free(cgen) + emit_unlock(cgen) + emit_return(typeInfo, api, cgen) + +def emit_manual_inline(typeInfo, api, cgen): + cgen.line("#include \"%s_encode_impl.cpp.inl\"" % api.name) + +def unwrap_vkCreateImage_pCreateInfo(): + def mapOp(cgen, orig, local): + cgen.stmt("sResourceTracker->unwrap_vkCreateImage_pCreateInfo(%s, %s)" % + (orig.paramName, local.paramName)) + return { "pCreateInfo" : { "mapOp" : mapOp } } + +def unwrap_vkBindImageMemory2_pBindInfos(): + def mapOp(cgen, orig, local): + cgen.stmt("sResourceTracker->unwrap_VkBindImageMemory2_pBindInfos(bindInfoCount, %s, %s)" % + (orig.paramName, local.paramName)) + return { "pBindInfos" : { "mapOp" : mapOp } } + +def unwrap_vkAcquireImageANDROID_nativeFenceFd(): + def mapOp(cgen, orig, local): + cgen.stmt("sResourceTracker->unwrap_vkAcquireImageANDROID_nativeFenceFd(%s, &%s)" % + (orig.paramName, local.paramName)) + return { "nativeFenceFd" : { "mapOp" : mapOp } } + +custom_encodes = { + "vkMapMemory" : emit_only_resource_event, + "vkUnmapMemory" : emit_only_resource_event, + "vkFlushMappedMemoryRanges" : encode_vkFlushMappedMemoryRanges, + "vkInvalidateMappedMemoryRanges" : encode_vkInvalidateMappedMemoryRanges, + "vkCreateImage" : emit_with_custom_unwrap(unwrap_vkCreateImage_pCreateInfo()), + "vkCreateImageWithRequirementsGOOGLE" : emit_with_custom_unwrap(unwrap_vkCreateImage_pCreateInfo()), + "vkBindImageMemory2": emit_with_custom_unwrap(unwrap_vkBindImageMemory2_pBindInfos()), + "vkAcquireImageANDROID" : emit_with_custom_unwrap(unwrap_vkAcquireImageANDROID_nativeFenceFd()), + "vkQueueFlushCommandsGOOGLE" : emit_manual_inline, +} + +class VulkanEncoder(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.typeInfo = typeInfo + + self.cgenHeader = CodeGen() + self.cgenHeader.incrIndent() + + self.cgenImpl = CodeGen() + + def onBegin(self,): + self.module.appendHeader(encoder_decl_preamble) + self.module.appendImpl(encoder_impl_preamble) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + api = copy.deepcopy(self.typeInfo.apis[name]) + api.parameters.append(makeVulkanTypeSimple(False, "uint32_t", 0, "doLock")) + + self.cgenHeader.stmt(self.cgenHeader.makeFuncProto(api)) + apiImpl = api.withModifiedName("VkEncoder::" + api.name) + + self.module.appendHeader(self.cgenHeader.swapCode()) + + def emit_function_impl(cgen): + emit_health_watchdog(api, cgen) + if api.name in custom_encodes.keys(): + custom_encodes[api.name](self.typeInfo, api, cgen) + else: + emit_default_encoding(self.typeInfo, api, cgen) + + self.module.appendImpl(self.cgenImpl.makeFuncImpl(apiImpl, emit_function_impl)) + + def onEnd(self,): + self.module.appendHeader(encoder_decl_postamble) + self.cgenHeader.decrIndent() diff --git a/src/gfxstream/codegen/scripts/cereal/extensionstructs.py b/src/gfxstream/codegen/scripts/cereal/extensionstructs.py new file mode 100644 index 00000000000..3c24d059757 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/extensionstructs.py @@ -0,0 +1,124 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import STRUCT_EXTENSION_PARAM +from .wrapperdefs import STRUCT_EXTENSION_PARAM_FOR_WRITE +from .wrapperdefs import EXTENSION_SIZE_API_NAME +from .wrapperdefs import EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME +from .wrapperdefs import STRUCT_TYPE_API_NAME + +class VulkanExtensionStructs(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.structTypeRetType = \ + makeVulkanTypeSimple(False, "uint32_t", 0) + + self.rootTypeVarName = "rootType" + self.rootTypeParam = \ + makeVulkanTypeSimple(False, "VkStructureType", + 0, self.rootTypeVarName) + self.structTypePrototype = \ + VulkanAPI(STRUCT_TYPE_API_NAME, + self.structTypeRetType, + [STRUCT_EXTENSION_PARAM]) + + self.extensionStructSizeRetType = \ + makeVulkanTypeSimple(False, "size_t", 0) + self.extensionStructSizePrototype = \ + VulkanAPI(EXTENSION_SIZE_API_NAME, + self.extensionStructSizeRetType, + [self.rootTypeParam, STRUCT_EXTENSION_PARAM]) + + self.streamFeaturesType = makeVulkanTypeSimple(False, "uint32_t", 0, "streamFeatures") + + self.extensionStructSizeWithStreamFeaturesPrototype = \ + VulkanAPI(EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, + self.extensionStructSizeRetType, + [self.streamFeaturesType, self.rootTypeParam, STRUCT_EXTENSION_PARAM]) + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendHeader(self.codegen.makeFuncDecl( + self.structTypePrototype)) + self.module.appendHeader(self.codegen.makeFuncDecl( + self.extensionStructSizePrototype)) + self.module.appendHeader(self.codegen.makeFuncDecl( + self.extensionStructSizeWithStreamFeaturesPrototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def castAsStruct(varName, typeName, const=True): + return "reinterpret_cast<%s%s*>(%s)" % \ + ("const " if const else "", typeName, varName) + + def structTypeImpl(cgen): + cgen.stmt( + "const uint32_t asStructType = *(%s)" % + (castAsStruct(STRUCT_EXTENSION_PARAM.paramName, "uint32_t"))) + cgen.stmt("return asStructType") + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.structTypePrototype, structTypeImpl)) + + def forEachExtensionReturnSize(ext, _, cgen): + cgen.stmt("return sizeof(%s)" % ext.name) + + def forEachExtensionReturnSizeProtectedByFeature(ext, _, cgen): + streamFeature = ext.getProtectStreamFeature() + if streamFeature is None: + cgen.stmt("return sizeof(%s)" % ext.name) + return + cgen.beginIf("%s & %s" % ("streamFeatures", streamFeature)) + cgen.stmt("return sizeof(%s)" % ext.name) + cgen.endIf() + cgen.beginElse() + cgen.stmt("return 0") + cgen.endIf() + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionStructSizePrototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + self.extensionStructSizeRetType, + STRUCT_EXTENSION_PARAM, + forEachExtensionReturnSize, autoBreak=False, + rootTypeVar=self.rootTypeParam))) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionStructSizeWithStreamFeaturesPrototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + self.extensionStructSizeRetType, + STRUCT_EXTENSION_PARAM, + forEachExtensionReturnSizeProtectedByFeature, autoBreak=False, + rootTypeVar=self.rootTypeParam))) diff --git a/src/gfxstream/codegen/scripts/cereal/frontend.py b/src/gfxstream/codegen/scripts/cereal/frontend.py new file mode 100644 index 00000000000..b182c0e7852 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/frontend.py @@ -0,0 +1,102 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen, VulkanAPIWrapper +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType + +from .wrapperdefs import VulkanWrapperGenerator + +from .wrapperdefs import API_PREFIX_VALIDATE +from .wrapperdefs import PARAMETERS_VALIDATE +from .wrapperdefs import VOID_TYPE +from .wrapperdefs import VALIDATE_RESULT_TYPE +from .wrapperdefs import VALIDATE_VAR_NAME +from .wrapperdefs import VALIDATE_GOOD_RESULT + +from .wrapperdefs import VULKAN_STREAM_TYPE +from .wrapperdefs import VULKAN_STREAM_VAR_NAME + +from .wrapperdefs import API_PREFIX_MARSHAL +from .wrapperdefs import API_PREFIX_FRONTEND + +# Frontend +class VulkanFrontend(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + def validateDefFunc(_codegen, _api): + # TODO + pass + + self.validateWrapper = \ + VulkanAPIWrapper( + API_PREFIX_VALIDATE, + PARAMETERS_VALIDATE, + VOID_TYPE, + validateDefFunc) + + def frontendDefFunc(codegen, api): + retTypeName = api.retType.typeName + + codegen.stmt( + "%s %s = %s" % (VALIDATE_RESULT_TYPE, VALIDATE_VAR_NAME, + VALIDATE_GOOD_RESULT)) + codegen.funcCall(None, API_PREFIX_VALIDATE + api.origName, + ["&%s" % VALIDATE_VAR_NAME] + list( + map(lambda p: p.paramName, api.parameters))) + + codegen.beginIf( + "%s != %s" % (VALIDATE_VAR_NAME, VALIDATE_GOOD_RESULT)) + if retTypeName == VALIDATE_RESULT_TYPE: + codegen.stmt("return %s" % VALIDATE_VAR_NAME) + elif retTypeName != "void": + codegen.stmt("return (%s)0" % retTypeName) + else: + codegen.stmt("return") + codegen.endIf() + + codegen.stmt("// VULKAN_STREAM_GET()") + codegen.stmt("%s* %s = nullptr" % (VULKAN_STREAM_TYPE, + VULKAN_STREAM_VAR_NAME)) + + retLhs = None + if retTypeName != "void": + retLhs = retTypeName + " res" + + codegen.funcCall(retLhs, API_PREFIX_MARSHAL + api.origName, + [VULKAN_STREAM_VAR_NAME] + list( + map(lambda p: p.paramName, api.parameters))) + + if retTypeName != "void": + codegen.stmt("return res") + + self.frontendWrapper = \ + VulkanAPIWrapper( + API_PREFIX_FRONTEND, + [], + None, + frontendDefFunc) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + self.module.appendHeader( + self.frontendWrapper.makeDecl(self.typeInfo, name)) + self.module.appendImpl( + self.validateWrapper.makeDefinition( + self.typeInfo, name, isStatic=True)) + self.module.appendImpl( + self.frontendWrapper.makeDefinition(self.typeInfo, name)) diff --git a/src/gfxstream/codegen/scripts/cereal/functable.py b/src/gfxstream/codegen/scripts/cereal/functable.py new file mode 100644 index 00000000000..016d6cac67a --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/functable.py @@ -0,0 +1,411 @@ +from .common.codegen import CodeGen, VulkanWrapperGenerator +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType +from .common.vulkantypes import EXCLUDED_APIS + +RESOURCE_TRACKER_ENTRIES = [ + "vkEnumerateInstanceExtensionProperties", + "vkEnumerateDeviceExtensionProperties", + "vkEnumeratePhysicalDevices", + "vkAllocateMemory", + "vkFreeMemory", + "vkCreateImage", + "vkDestroyImage", + "vkGetImageMemoryRequirements", + "vkGetImageMemoryRequirements2", + "vkGetImageMemoryRequirements2KHR", + "vkBindImageMemory", + "vkBindImageMemory2", + "vkBindImageMemory2KHR", + "vkCreateBuffer", + "vkDestroyBuffer", + "vkGetBufferMemoryRequirements", + "vkGetBufferMemoryRequirements2", + "vkGetBufferMemoryRequirements2KHR", + "vkBindBufferMemory", + "vkBindBufferMemory2", + "vkBindBufferMemory2KHR", + "vkCreateSemaphore", + "vkDestroySemaphore", + "vkQueueSubmit", + "vkQueueSubmit2", + "vkQueueWaitIdle", + "vkImportSemaphoreFdKHR", + "vkGetSemaphoreFdKHR", + # Warning: These need to be defined in vk.xml (currently no-op) { + "vkGetMemoryFuchsiaHandleKHR", + "vkGetMemoryFuchsiaHandlePropertiesKHR", + "vkGetSemaphoreFuchsiaHandleKHR", + "vkImportSemaphoreFuchsiaHandleKHR", + # } end Warning: These need to be defined in vk.xml (currently no-op) + "vkGetAndroidHardwareBufferPropertiesANDROID", + "vkGetMemoryAndroidHardwareBufferANDROID", + "vkCreateSamplerYcbcrConversion", + "vkDestroySamplerYcbcrConversion", + "vkCreateSamplerYcbcrConversionKHR", + "vkDestroySamplerYcbcrConversionKHR", + "vkUpdateDescriptorSetWithTemplate", + "vkGetPhysicalDeviceImageFormatProperties2", + "vkGetPhysicalDeviceImageFormatProperties2KHR", + "vkBeginCommandBuffer", + "vkEndCommandBuffer", + "vkResetCommandBuffer", + "vkCreateImageView", + "vkCreateSampler", + "vkGetPhysicalDeviceExternalFenceProperties", + "vkGetPhysicalDeviceExternalFencePropertiesKHR", + "vkGetPhysicalDeviceExternalBufferProperties", + "vkGetPhysicalDeviceExternalBufferPropertiesKHR", + "vkCreateFence", + "vkResetFences", + "vkImportFenceFdKHR", + "vkGetFenceFdKHR", + "vkWaitForFences", + "vkCreateDescriptorPool", + "vkDestroyDescriptorPool", + "vkResetDescriptorPool", + "vkAllocateDescriptorSets", + "vkFreeDescriptorSets", + "vkCreateDescriptorSetLayout", + "vkUpdateDescriptorSets", + "vkCmdExecuteCommands", + "vkCmdBindDescriptorSets", + "vkDestroyDescriptorSetLayout", + "vkAllocateCommandBuffers", + "vkQueueSignalReleaseImageANDROID", + "vkCmdPipelineBarrier", + "vkCreateGraphicsPipelines", + # Fuchsia + "vkGetMemoryZirconHandleFUCHSIA", + "vkGetMemoryZirconHandlePropertiesFUCHSIA", + "vkGetSemaphoreZirconHandleFUCHSIA", + "vkImportSemaphoreZirconHandleFUCHSIA", + "vkCreateBufferCollectionFUCHSIA", + "vkDestroyBufferCollectionFUCHSIA", + "vkSetBufferCollectionImageConstraintsFUCHSIA", + "vkSetBufferCollectionBufferConstraintsFUCHSIA", + "vkGetBufferCollectionPropertiesFUCHSIA", +] + +SUCCESS_VAL = { + "VkResult" : ["VK_SUCCESS"], +} + +POSTPROCESSES = { + "vkResetCommandPool" : """if (vkResetCommandPool_VkResult_return == VK_SUCCESS) { + ResourceTracker::get()->resetCommandPoolStagingInfo(commandPool); + }""", + "vkAllocateCommandBuffers" : """if (vkAllocateCommandBuffers_VkResult_return == VK_SUCCESS) { + ResourceTracker::get()->addToCommandPool(pAllocateInfo->commandPool, pAllocateInfo->commandBufferCount, pCommandBuffers); + }""", +} + +def is_cmdbuf_dispatch(api): + return "VkCommandBuffer" == api.parameters[0].typeName + +def is_queue_dispatch(api): + return "VkQueue" == api.parameters[0].typeName + +class VulkanFuncTable(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + self.typeInfo = typeInfo + self.cgen = CodeGen() + self.entries = [] + self.entryFeatures = [] + self.cmdToFeatureType = {} + self.feature = None + self.featureType = None + + def onBegin(self,): + cgen = self.cgen + cgen.line("static void sOnInvalidDynamicallyCheckedCall(const char* apiname, const char* neededFeature)") + cgen.beginBlock() + cgen.stmt("ALOGE(\"invalid call to %s: %s not supported\", apiname, neededFeature)") + cgen.stmt("abort()") + cgen.endBlock() + self.module.appendImpl(cgen.swapCode()) + pass + + def onBeginFeature(self, featureName, featureType): + self.feature = featureName + self.featureType = featureType + + def onEndFeature(self): + self.feature = None + self.featureType = None + + def onFeatureNewCmd(self, name): + self.cmdToFeatureType[name] = self.featureType + + def onGenCmd(self, cmdinfo, name, alias): + typeInfo = self.typeInfo + cgen = self.cgen + api = typeInfo.apis[name] + self.entries.append(api) + self.entryFeatures.append(self.feature) + + def genEncoderOrResourceTrackerCall(cgen, api, declareResources=True): + cgen.stmt("AEMU_SCOPED_TRACE(\"%s\")" % api.name) + + if is_cmdbuf_dispatch(api): + cgen.stmt("auto vkEnc = ResourceTracker::getCommandBufferEncoder(commandBuffer)") + elif is_queue_dispatch(api): + cgen.stmt("auto vkEnc = ResourceTracker::getQueueEncoder(queue)") + else: + cgen.stmt("auto vkEnc = ResourceTracker::getThreadLocalEncoder()") + callLhs = None + retTypeName = api.getRetTypeExpr() + if retTypeName != "void": + retVar = api.getRetVarExpr() + cgen.stmt("%s %s = (%s)0" % (retTypeName, retVar, retTypeName)) + callLhs = retVar + + if name in RESOURCE_TRACKER_ENTRIES: + if declareResources: + cgen.stmt("auto resources = ResourceTracker::get()") + cgen.funcCall( + callLhs, "resources->" + "on_" + api.name, + ["vkEnc"] + SUCCESS_VAL.get(retTypeName, []) + \ + [p.paramName for p in api.parameters]) + else: + cgen.funcCall( + callLhs, "vkEnc->" + api.name, [p.paramName for p in api.parameters] + ["true /* do lock */"]) + + if name in POSTPROCESSES: + cgen.line(POSTPROCESSES[name]) + + if retTypeName != "void": + cgen.stmt("return %s" % retVar) + + + api_entry = api.withModifiedName("entry_" + api.name) + + cgen.line("static " + self.cgen.makeFuncProto(api_entry)) + cgen.beginBlock() + genEncoderOrResourceTrackerCall(cgen, api) + cgen.endBlock() + + if self.isDeviceDispatch(api) and self.feature != "VK_VERSION_1_0": + api_entry_dyn_check = api.withModifiedName("dynCheck_entry_" + api.name) + cgen.line("static " + self.cgen.makeFuncProto(api_entry_dyn_check)) + cgen.beginBlock() + if self.feature == "VK_VERSION_1_3": + cgen.stmt("auto resources = ResourceTracker::get()") + if "VkCommandBuffer" == api.parameters[0].typeName: + cgen.stmt("VkDevice device = resources->getDevice(commandBuffer)") + cgen.beginIf("resources->getApiVersionFromDevice(device) < VK_API_VERSION_1_3") + cgen.stmt("sOnInvalidDynamicallyCheckedCall(\"%s\", \"%s\")" % (api.name, self.feature)) + cgen.endIf() + elif self.feature == "VK_VERSION_1_2": + cgen.stmt("auto resources = ResourceTracker::get()") + if "VkCommandBuffer" == api.parameters[0].typeName: + cgen.stmt("VkDevice device = resources->getDevice(commandBuffer)") + cgen.beginIf("resources->getApiVersionFromDevice(device) < VK_API_VERSION_1_2") + cgen.stmt("sOnInvalidDynamicallyCheckedCall(\"%s\", \"%s\")" % (api.name, self.feature)) + cgen.endIf() + elif self.feature == "VK_VERSION_1_1": + cgen.stmt("auto resources = ResourceTracker::get()") + if "VkCommandBuffer" == api.parameters[0].typeName: + cgen.stmt("VkDevice device = resources->getDevice(commandBuffer)") + cgen.beginIf("resources->getApiVersionFromDevice(device) < VK_API_VERSION_1_1") + cgen.stmt("sOnInvalidDynamicallyCheckedCall(\"%s\", \"%s\")" % (api.name, self.feature)) + cgen.endIf() + elif self.feature != "VK_VERSION_1_0": + cgen.stmt("auto resources = ResourceTracker::get()") + if "VkCommandBuffer" == api.parameters[0].typeName: + cgen.stmt("VkDevice device = resources->getDevice(commandBuffer);") + cgen.beginIf("!resources->hasDeviceExtension(device, \"%s\")" % self.feature) + cgen.stmt("sOnInvalidDynamicallyCheckedCall(\"%s\", \"%s\")" % (api.name, self.feature)) + cgen.endIf() + else: + print("About to generate a frivolous api!: dynCheck entry: %s" % api.name) + raise + genEncoderOrResourceTrackerCall(cgen, api, declareResources = False) + cgen.endBlock() + + self.module.appendImpl(cgen.swapCode()) + + def onEnd(self,): + getProcAddressDecl = "void* goldfish_vulkan_get_proc_address(const char* name)" + self.module.appendHeader(getProcAddressDecl + ";\n") + self.module.appendImpl(getProcAddressDecl) + self.cgen.beginBlock() + + prevFeature = None + for e, f in zip(self.entries, self.entryFeatures): + featureEndif = prevFeature is not None and (f != prevFeature) + featureif = not featureEndif and (f != prevFeature) + + if featureEndif: + self.cgen.leftline("#endif") + self.cgen.leftline("#ifdef %s" % f) + + if featureif: + self.cgen.leftline("#ifdef %s" % f) + + self.cgen.beginIf("!strcmp(name, \"%s\")" % e.name) + if e.name in EXCLUDED_APIS: + self.cgen.stmt("return nullptr") + elif f == "VK_VERSION_1_3": + self.cgen.stmt("return nullptr") + elif f == "VK_VERSION_1_2": + self.cgen.stmt("return nullptr") + elif f == "VK_VERSION_1_1": + self.cgen.stmt("return nullptr") + elif f != "VK_VERSION_1_0": + self.cgen.stmt("return nullptr") + else: + self.cgen.stmt("return (void*)%s" % ("entry_" + e.name)) + self.cgen.endIf() + prevFeature = f + + self.cgen.leftline("#endif") + + self.cgen.stmt("return nullptr") + self.cgen.endBlock() + self.module.appendImpl(self.cgen.swapCode()) + + getInstanceProcAddressDecl = "void* goldfish_vulkan_get_instance_proc_address(VkInstance instance, const char* name)" + self.module.appendHeader(getInstanceProcAddressDecl + ";\n") + self.module.appendImpl(getInstanceProcAddressDecl) + self.cgen.beginBlock() + + self.cgen.stmt( + "auto resources = ResourceTracker::get()") + self.cgen.stmt( + "bool has1_1OrHigher = resources->getApiVersionFromInstance(instance) >= VK_API_VERSION_1_1") + self.cgen.stmt( + "bool has1_2OrHigher = resources->getApiVersionFromInstance(instance) >= VK_API_VERSION_1_2") + self.cgen.stmt( + "bool has1_3OrHigher = resources->getApiVersionFromInstance(instance) >= VK_API_VERSION_1_3") + + prevFeature = None + for e, f in zip(self.entries, self.entryFeatures): + featureEndif = prevFeature is not None and (f != prevFeature) + featureif = not featureEndif and (f != prevFeature) + + if featureEndif: + self.cgen.leftline("#endif") + self.cgen.leftline("#ifdef %s" % f) + + if featureif: + self.cgen.leftline("#ifdef %s" % f) + + self.cgen.beginIf("!strcmp(name, \"%s\")" % e.name) + + entryPointExpr = "(void*)%s" % ("entry_" + e.name) + + if e.name in EXCLUDED_APIS: + self.cgen.stmt("return nullptr") + elif f == "VK_VERSION_1_3": + if self.isDeviceDispatch(e): + self.cgen.stmt("return (void*)dynCheck_entry_%s" % e.name) + else: + self.cgen.stmt( \ + "return has1_3OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f == "VK_VERSION_1_2": + if self.isDeviceDispatch(e): + self.cgen.stmt("return (void*)dynCheck_entry_%s" % e.name) + else: + self.cgen.stmt( \ + "return has1_2OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f == "VK_VERSION_1_1": + if self.isDeviceDispatch(e): + self.cgen.stmt("return (void*)dynCheck_entry_%s" % e.name) + else: + self.cgen.stmt( \ + "return has1_1OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f != "VK_VERSION_1_0": + entryNeedsInstanceExtensionCheck = self.cmdToFeatureType[e.name] == "instance" + + entryPrefix = "dynCheck_" if self.isDeviceDispatch(e) else "" + entryPointExpr = "(void*)%sentry_%s" % (entryPrefix, e.name) + + if entryNeedsInstanceExtensionCheck: + self.cgen.stmt("bool hasExt = resources->hasInstanceExtension(instance, \"%s\")" % f) + self.cgen.stmt("return hasExt ? %s : nullptr" % entryPointExpr) + else: + # TODO(b/236246382): We need to check the device extension support here. + self.cgen.stmt("// TODO(b/236246382): Check support for device extension"); + self.cgen.stmt("return %s" % entryPointExpr) + + else: + self.cgen.stmt("return %s" % entryPointExpr) + self.cgen.endIf() + prevFeature = f + + self.cgen.leftline("#endif") + + self.cgen.stmt("return nullptr") + self.cgen.endBlock() + self.module.appendImpl(self.cgen.swapCode()) + + getDeviceProcAddressDecl = "void* goldfish_vulkan_get_device_proc_address(VkDevice device, const char* name)" + self.module.appendHeader(getDeviceProcAddressDecl + ";\n") + self.module.appendImpl(getDeviceProcAddressDecl) + self.cgen.beginBlock() + + self.cgen.stmt( + "auto resources = ResourceTracker::get()") + self.cgen.stmt( + "bool has1_1OrHigher = resources->getApiVersionFromDevice(device) >= VK_API_VERSION_1_1") + self.cgen.stmt( + "bool has1_2OrHigher = resources->getApiVersionFromDevice(device) >= VK_API_VERSION_1_2") + self.cgen.stmt( + "bool has1_3OrHigher = resources->getApiVersionFromDevice(device) >= VK_API_VERSION_1_3") + prevFeature = None + for e, f in zip(self.entries, self.entryFeatures): + featureEndif = prevFeature is not None and (f != prevFeature) + featureif = not featureEndif and (f != prevFeature) + + if featureEndif: + self.cgen.leftline("#endif") + self.cgen.leftline("#ifdef %s" % f) + + if featureif: + self.cgen.leftline("#ifdef %s" % f) + + self.cgen.beginIf("!strcmp(name, \"%s\")" % e.name) + + entryPointExpr = "(void*)%s" % ("entry_" + e.name) + + if e.name in EXCLUDED_APIS: + self.cgen.stmt("return nullptr") + elif f == "VK_VERSION_1_3": + self.cgen.stmt( \ + "return has1_3OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f == "VK_VERSION_1_2": + self.cgen.stmt( \ + "return has1_2OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f == "VK_VERSION_1_1": + self.cgen.stmt( \ + "return has1_1OrHigher ? %s : nullptr" % \ + entryPointExpr) + elif f != "VK_VERSION_1_0": + self.cgen.stmt( \ + "bool hasExt = resources->hasDeviceExtension(device, \"%s\")" % f) + self.cgen.stmt("return hasExt ? %s : nullptr" % entryPointExpr) + else: + self.cgen.stmt("return %s" % entryPointExpr) + self.cgen.endIf() + prevFeature = f + + self.cgen.leftline("#endif") + + self.cgen.stmt("return nullptr") + self.cgen.endBlock() + + self.module.appendImpl(self.cgen.swapCode()) + + def isDeviceDispatch(self, api): + # TODO(230793667): improve the heuristic and just use "cmdToFeatureType" + return (len(api.parameters) > 0 and + "VkDevice" == api.parameters[0].typeName) or ( + "VkCommandBuffer" == api.parameters[0].typeName and + self.cmdToFeatureType.get(api.name, "") == "device") diff --git a/src/gfxstream/codegen/scripts/cereal/handlemap.py b/src/gfxstream/codegen/scripts/cereal/handlemap.py new file mode 100644 index 00000000000..6360e37e018 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/handlemap.py @@ -0,0 +1,264 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE + +class HandleMapCodegen(VulkanTypeIterator): + def __init__(self, cgen, inputVar, handlemapVarName, prefix, isHandleFunc): + self.cgen = cgen + self.inputVar = inputVar + self.prefix = prefix + self.handlemapVarName = handlemapVarName + + def makeAccess(varName, asPtr = True): + return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr) + + def makeLengthAccess(varName): + return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName) + + def makeLengthAccessGuard(varName): + return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName) + + self.exprAccessor = makeAccess(self.inputVar) + self.exprAccessorValue = makeAccess(self.inputVar, asPtr = False) + self.lenAccessor = makeLengthAccess(self.inputVar) + self.lenAccessorGuard = makeLengthAccessGuard(self.inputVar) + + self.checked = False + self.isHandleFunc = isHandleFunc + + def needSkip(self, vulkanType): + return False + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def asNonConstCast(self, access, vulkanType): + if vulkanType.staticArrExpr: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + elif vulkanType.accessibleAsPointer(): + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access) + else: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + return casted + + def onCheck(self, vulkanType): + pass + + def endCheck(self, vulkanType): + pass + + def onCompoundType(self, vulkanType): + + if self.needSkip(vulkanType): + self.cgen.line("// TODO: Unsupported : %s" % + self.cgen.makeCTypeDecl(vulkanType)) + return + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + isPtr = vulkanType.pointerIndirectionLevels > 0 + + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + + if isPtr: + self.cgen.beginIf(access) + + if lenAccess is not None: + + loopVar = "i" + access = "%s + %s" % (access, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess) + forIncr = "++%s" % loopVar + + self.cgen.beginFor(forInit, forCond, forIncr) + + accessCasted = self.asNonConstCast(access, vulkanType) + self.cgen.funcCall(None, self.prefix + vulkanType.typeName, + [self.handlemapVarName, accessCasted]) + + if lenAccess is not None: + self.cgen.endFor() + + if isPtr: + self.cgen.endIf() + + if lenAccessGuard is not None: + self.cgen.endIf() + + def onString(self, vulkanType): + pass + + def onStringArray(self, vulkanType): + pass + + def onStaticArr(self, vulkanType): + if not self.isHandleFunc(vulkanType): + return + + accessLhs = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + + self.cgen.stmt("%s->mapHandles_%s(%s%s, %s)" % \ + (self.handlemapVarName, vulkanType.typeName, + self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), + accessLhs, lenAccess)) + + def onStructExtension(self, vulkanType): + access = self.exprAccessor(vulkanType) + + castedAccessExpr = "(%s)(%s)" % ("void*", access) + self.cgen.beginIf(access) + self.cgen.funcCall(None, self.prefix + "extension_struct", + [self.handlemapVarName, castedAccessExpr]) + self.cgen.endIf() + + def onPointer(self, vulkanType): + if self.needSkip(vulkanType): + return + + if not self.isHandleFunc(vulkanType): + return + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccess = "1" if lenAccess is None else lenAccess + + self.cgen.beginIf(access) + + self.cgen.stmt( \ + "%s->mapHandles_%s(%s%s, %s)" % \ + (self.handlemapVarName, + vulkanType.typeName, + self.makeCastExpr(vulkanType.getForNonConstAccess()), + access, + lenAccess)) + + self.cgen.endIf() + + def onValue(self, vulkanType): + if not self.isHandleFunc(vulkanType): + return + access = self.exprAccessor(vulkanType) + self.cgen.stmt( + "%s->mapHandles_%s(%s%s)" % \ + (self.handlemapVarName, vulkanType.typeName, + self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), + access)) + +class VulkanHandleMap(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.handlemapPrefix = "handlemap_" + self.toMapVar = "toMap" + self.handlemapVarName = "handlemap" + self.handlemapParam = \ + makeVulkanTypeSimple(False, "VulkanHandleMapping", 1, + self.handlemapVarName) + self.voidType = makeVulkanTypeSimple(False, "void", 0) + + self.handlemapCodegen = \ + HandleMapCodegen( + None, + self.toMapVar, + self.handlemapVarName, + self.handlemapPrefix, + lambda vtype : typeInfo.isHandleType(vtype.typeName)) + + self.knownDefs = {} + + self.extensionHandlemapPrototype = \ + VulkanAPI(self.handlemapPrefix + "extension_struct", + self.voidType, + [self.handlemapParam, STRUCT_EXTENSION_PARAM_FOR_WRITE]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.codegen.makeFuncDecl( + self.extensionHandlemapPrototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + self.module.appendHeader( + self.codegen.makeFuncAlias(self.handlemapPrefix + name, + self.handlemapPrefix + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + typeFromName = \ + lambda varname: \ + makeVulkanTypeSimple(varname == "from", name, 1, varname) + + handlemapParams = \ + [self.handlemapParam] + \ + list(map(typeFromName, [self.toMapVar])) + + handlemapPrototype = \ + VulkanAPI(self.handlemapPrefix + name, + self.voidType, + handlemapParams) + + def funcDefGenerator(cgen): + self.handlemapCodegen.cgen = cgen + for p in handlemapParams: + cgen.stmt("(void)%s" % p.paramName) + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, + self.handlemapCodegen) + + self.module.appendHeader( + self.codegen.makeFuncDecl(handlemapPrototype)) + self.module.appendImpl( + self.codegen.makeFuncImpl(handlemapPrototype, funcDefGenerator)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def forEachExtensionHandlemap(ext, castedAccess, cgen): + cgen.funcCall(None, self.handlemapPrefix + ext.name, + [self.handlemapVarName, castedAccess]) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionHandlemapPrototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + self.voidType, + STRUCT_EXTENSION_PARAM_FOR_WRITE, + forEachExtensionHandlemap))) diff --git a/src/gfxstream/codegen/scripts/cereal/marshaling.py b/src/gfxstream/codegen/scripts/cereal/marshaling.py new file mode 100644 index 00000000000..c5fd350e45a --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/marshaling.py @@ -0,0 +1,1035 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import copy +import hashlib, sys + +from .common.codegen import CodeGen, VulkanAPIWrapper +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator, Atom, FuncExpr, FuncExprVal, FuncLambda + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import VULKAN_STREAM_VAR_NAME +from .wrapperdefs import ROOT_TYPE_VAR_NAME, ROOT_TYPE_PARAM +from .wrapperdefs import STREAM_RET_TYPE +from .wrapperdefs import MARSHAL_INPUT_VAR_NAME +from .wrapperdefs import UNMARSHAL_INPUT_VAR_NAME +from .wrapperdefs import PARAMETERS_MARSHALING +from .wrapperdefs import PARAMETERS_MARSHALING_GUEST +from .wrapperdefs import STYPE_OVERRIDE +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME +from .wrapperdefs import API_PREFIX_MARSHAL +from .wrapperdefs import API_PREFIX_UNMARSHAL + +from .marshalingdefs import KNOWN_FUNCTION_OPCODES, CUSTOM_MARSHAL_TYPES + +class VulkanMarshalingCodegen(VulkanTypeIterator): + + def __init__(self, + cgen, + streamVarName, + rootTypeVarName, + inputVarName, + marshalPrefix, + direction = "write", + forApiOutput = False, + dynAlloc = False, + mapHandles = True, + handleMapOverwrites = False, + doFiltering = True): + self.cgen = cgen + self.direction = direction + self.processSimple = "write" if self.direction == "write" else "read" + self.forApiOutput = forApiOutput + + self.checked = False + + self.streamVarName = streamVarName + self.rootTypeVarName = rootTypeVarName + self.inputVarName = inputVarName + self.marshalPrefix = marshalPrefix + + self.exprAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = True) + self.exprValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = False) + self.exprPrimitiveValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = False) + self.lenAccessor = lambda t: self.cgen.generalLengthAccess(t, parentVarName = self.inputVarName) + self.lenAccessorGuard = lambda t: self.cgen.generalLengthAccessGuard( + t, parentVarName=self.inputVarName) + self.filterVarAccessor = lambda t: self.cgen.filterVarAccess(t, parentVarName = self.inputVarName) + + self.dynAlloc = dynAlloc + self.mapHandles = mapHandles + self.handleMapOverwrites = handleMapOverwrites + self.doFiltering = doFiltering + + def getTypeForStreaming(self, vulkanType): + res = copy(vulkanType) + + if not vulkanType.accessibleAsPointer(): + res = res.getForAddressAccess() + + if vulkanType.staticArrExpr: + res = res.getForAddressAccess() + + if self.direction == "write": + return res + else: + return res.getForNonConstAccess() + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def genStreamCall(self, vulkanType, toStreamExpr, sizeExpr): + varname = self.streamVarName + func = self.processSimple + cast = self.makeCastExpr(self.getTypeForStreaming(vulkanType)) + + self.cgen.stmt( + "%s->%s(%s%s, %s)" % (varname, func, cast, toStreamExpr, sizeExpr)) + + def genPrimitiveStreamCall(self, vulkanType, access): + varname = self.streamVarName + + self.cgen.streamPrimitive( + self.typeInfo, + varname, + access, + vulkanType, + direction=self.direction) + + def genHandleMappingCall(self, vulkanType, access, lenAccess): + + if lenAccess is None: + lenAccess = "1" + handle64Bytes = "8" + else: + handle64Bytes = "%s * 8" % lenAccess + + handle64Var = self.cgen.var() + if lenAccess != "1": + self.cgen.beginIf(lenAccess) + self.cgen.stmt("uint64_t* %s" % handle64Var) + self.cgen.stmt( + "%s->alloc((void**)&%s, %s * 8)" % \ + (self.streamVarName, handle64Var, lenAccess)) + handle64VarAccess = handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 1, paramName=handle64Var) + else: + self.cgen.stmt("uint64_t %s" % handle64Var) + handle64VarAccess = "&%s" % handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 0, paramName=handle64Var) + + if self.direction == "write": + if self.handleMapOverwrites: + self.cgen.stmt( + "static_assert(8 == sizeof(%s), \"handle map overwrite requires %s to be 8 bytes long\")" % \ + (vulkanType.typeName, vulkanType.typeName)) + self.cgen.stmt( + "%s->handleMapping()->mapHandles_%s((%s*)%s, %s)" % + (self.streamVarName, vulkanType.typeName, vulkanType.typeName, + access, lenAccess)) + self.genStreamCall(vulkanType, access, "8 * %s" % lenAccess) + else: + self.cgen.stmt( + "%s->handleMapping()->mapHandles_%s_u64(%s, %s, %s)" % + (self.streamVarName, vulkanType.typeName, + access, + handle64VarAccess, lenAccess)) + self.genStreamCall(handle64VarType, handle64VarAccess, handle64Bytes) + else: + self.genStreamCall(handle64VarType, handle64VarAccess, handle64Bytes) + self.cgen.stmt( + "%s->handleMapping()->mapHandles_u64_%s(%s, %s%s, %s)" % + (self.streamVarName, vulkanType.typeName, + handle64VarAccess, + self.makeCastExpr(vulkanType.getForNonConstAccess()), access, + lenAccess)) + + if lenAccess != "1": + self.cgen.endIf() + + def doAllocSpace(self, vulkanType): + if self.dynAlloc and self.direction == "read": + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + sizeof = self.cgen.sizeofExpr( \ + vulkanType.getForValueAccess()) + if lenAccess: + bytesExpr = "%s * %s" % (lenAccess, sizeof) + else: + bytesExpr = sizeof + + self.cgen.stmt( \ + "%s->alloc((void**)&%s, %s)" % + (self.streamVarName, + access, bytesExpr)) + + def getOptionalStringFeatureExpr(self, vulkanType): + streamFeature = vulkanType.getProtectStreamFeature() + if streamFeature is None: + return None + return "%s->getFeatureBits() & %s" % (self.streamVarName, streamFeature) + + def onCheck(self, vulkanType): + + if self.forApiOutput: + return + + featureExpr = self.getOptionalStringFeatureExpr(vulkanType); + + self.checked = True + + access = self.exprAccessor(vulkanType) + + needConsistencyCheck = False + + self.cgen.line("// WARNING PTR CHECK") + if (self.dynAlloc and self.direction == "read") or self.direction == "write": + checkAccess = self.exprAccessor(vulkanType) + addrExpr = "&" + checkAccess + sizeExpr = self.cgen.sizeofExpr(vulkanType) + else: + checkName = "check_%s" % vulkanType.paramName + self.cgen.stmt("%s %s" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName = False), checkName)) + checkAccess = checkName + addrExpr = "&" + checkAccess + sizeExpr = self.cgen.sizeofExpr(vulkanType) + needConsistencyCheck = True + + if featureExpr is not None: + self.cgen.beginIf(featureExpr) + + self.genPrimitiveStreamCall( + vulkanType, + checkAccess) + + if featureExpr is not None: + self.cgen.endIf() + + if featureExpr is not None: + self.cgen.beginIf("(!(%s) || %s)" % (featureExpr, access)) + else: + self.cgen.beginIf(access) + + if needConsistencyCheck and featureExpr is None: + self.cgen.beginIf("!(%s)" % checkName) + self.cgen.stmt( + "fprintf(stderr, \"fatal: %s inconsistent between guest and host\\n\")" % (access)) + self.cgen.endIf() + + + def onCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.beginIf("%s->getFeatureBits() & VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT" % self.streamVarName) + self.onCheck(vulkanType) + + def endCheckWithNullOptionalStringFeature(self, vulkanType): + self.endCheck(vulkanType) + self.cgen.endIf() + self.cgen.beginElse() + + def finalCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.endElse() + + def endCheck(self, vulkanType): + + if self.checked: + self.cgen.endIf() + self.checked = False + + def genFilterFunc(self, filterfunc, env): + + def loop(expr, lambdaEnv={}): + def do_func(expr): + fnamestr = expr.name.name + if "not" == fnamestr: + return "!(%s)" % (loop(expr.args[0], lambdaEnv)) + if "eq" == fnamestr: + return "(%s == %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "and" == fnamestr: + return "(%s && %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "or" == fnamestr: + return "(%s || %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "bitwise_and" == fnamestr: + return "(%s & %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "getfield" == fnamestr: + ptrlevels = get_ptrlevels(expr.args[0].val.name) + if ptrlevels == 0: + return "%s.%s" % (loop(expr.args[0], lambdaEnv), expr.args[1].val) + else: + return "(%s(%s)).%s" % ("*" * ptrlevels, loop(expr.args[0], lambdaEnv), expr.args[1].val) + + if "if" == fnamestr: + return "((%s) ? (%s) : (%s))" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv), loop(expr.args[2], lambdaEnv)) + + return "%s(%s)" % (fnamestr, ", ".join(map(lambda e: loop(e, lambdaEnv), expr.args))) + + def do_expratom(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return atomname + + enventry = env.get(atomname, None) + if None != enventry: + return self.getEnvAccessExpr(atomname) + return atomname + + def get_ptrlevels(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return 0 + + enventry = env.get(atomname, None) + if None != enventry: + return self.getPointerIndirectionLevels(atomname) + + return 0 + + def do_exprval(expr, lambdaEnv= {}): + expratom = expr.val + + if Atom == type(expratom): + return do_expratom(expratom.name, lambdaEnv) + + return "%s" % expratom + + def do_lambda(expr, lambdaEnv= {}): + params = expr.vs + body = expr.body + newEnv = {} + + for (k, v) in lambdaEnv.items(): + newEnv[k] = v + + for p in params: + newEnv[p.name] = p.typ + + return "[](%s) { return %s; }" % (", ".join(list(map(lambda p: "%s %s" % (p.typ, p.name), params))), loop(body, lambdaEnv=newEnv)) + + if FuncExpr == type(expr): + return do_func(expr) + if FuncLambda == type(expr): + return do_lambda(expr) + elif FuncExprVal == type(expr): + return do_exprval(expr) + + return loop(filterfunc) + + def beginFilterGuard(self, vulkanType): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + filterVarAccess = self.getEnvAccessExpr(vulkanType.filterVar) + + filterValsExpr = None + filterFuncExpr = None + filterExpr = None + + filterFeature = "%s->getFeatureBits() & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.streamVarName + + if None != vulkanType.filterVals: + filterValsExpr = " || ".join(map(lambda filterval: "(%s == %s)" % (filterval, filterVarAccess), vulkanType.filterVals)) + + if None != vulkanType.filterFunc: + filterFuncExpr = self.genFilterFunc(vulkanType.filterFunc, self.currentStructInfo.environment) + + if None != filterValsExpr and None != filterFuncExpr: + filterExpr = "%s || %s" % (filterValsExpr, filterFuncExpr) + elif None == filterValsExpr and None == filterFuncExpr: + # Assume is bool + self.cgen.beginIf(filterVarAccess) + elif None != filterValsExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterValsExpr)) + elif None != filterFuncExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterFuncExpr)) + + def endFilterGuard(self, vulkanType, cleanupExpr=None): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + if cleanupExpr == None: + self.cgen.endIf() + else: + self.cgen.endIf() + self.cgen.beginElse() + self.cgen.stmt(cleanupExpr) + self.cgen.endElse() + + def getEnvAccessExpr(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + envAccess = self.exprValueAccessor(list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0]) + else: + envAccess = varName + return envAccess + + return None + + def getPointerIndirectionLevels(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + return list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0].pointerIndirectionLevels + else: + return 0 + return 0 + + return 0 + + + def onCompoundType(self, vulkanType): + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.beginFilterGuard(vulkanType) + + if vulkanType.pointerIndirectionLevels > 0: + self.doAllocSpace(vulkanType) + + if lenAccess is not None: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + loopVar = "i" + access = "%s + %s" % (access, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess) + forIncr = "++%s" % loopVar + self.cgen.beginFor(forInit, forCond, forIncr) + + accessWithCast = "%s(%s)" % (self.makeCastExpr( + self.getTypeForStreaming(vulkanType)), access) + + callParams = [self.streamVarName, self.rootTypeVarName, accessWithCast] + + for (bindName, localName) in vulkanType.binds.items(): + callParams.append(self.getEnvAccessExpr(localName)) + + self.cgen.funcCall(None, self.marshalPrefix + vulkanType.typeName, + callParams) + + if lenAccess is not None: + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + + if self.direction == "read": + self.endFilterGuard(vulkanType, "%s = 0" % self.exprAccessor(vulkanType)) + else: + self.endFilterGuard(vulkanType) + + def onString(self, vulkanType): + + access = self.exprAccessor(vulkanType) + + if self.direction == "write": + self.cgen.stmt("%s->putString(%s)" % (self.streamVarName, access)) + else: + castExpr = \ + self.makeCastExpr( \ + self.getTypeForStreaming( \ + vulkanType.getForAddressAccess())) + + self.cgen.stmt( \ + "%s->loadStringInPlace(%s&%s)" % (self.streamVarName, castExpr, access)) + + def onStringArray(self, vulkanType): + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + + if self.direction == "write": + self.cgen.stmt("saveStringArray(%s, %s, %s)" % (self.streamVarName, + access, lenAccess)) + else: + castExpr = \ + self.makeCastExpr( \ + self.getTypeForStreaming( \ + vulkanType.getForAddressAccess())) + + self.cgen.stmt("%s->loadStringArrayInPlace(%s&%s)" % (self.streamVarName, castExpr, access)) + + def onStaticArr(self, vulkanType): + access = self.exprValueAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + finalLenExpr = "%s * %s" % (lenAccess, self.cgen.sizeofExpr(vulkanType)) + self.genStreamCall(vulkanType, access, finalLenExpr) + + # Old version VkEncoder may have some sType values conflict with VkDecoder + # of new versions. For host decoder, it should not carry the incorrect old + # sType values to the |forUnmarshaling| struct. Instead it should overwrite + # the sType value. + def overwriteSType(self, vulkanType): + if self.direction == "read": + sTypeParam = copy(vulkanType) + sTypeParam.paramName = "sType" + sTypeAccess = self.exprAccessor(sTypeParam) + + typeName = vulkanType.parent.typeName + if typeName in STYPE_OVERRIDE: + self.cgen.stmt("%s = %s" % + (sTypeAccess, STYPE_OVERRIDE[typeName])) + + def onStructExtension(self, vulkanType): + self.overwriteSType(vulkanType) + + sTypeParam = copy(vulkanType) + sTypeParam.paramName = "sType" + + access = self.exprAccessor(vulkanType) + sizeVar = "%s_size" % vulkanType.paramName + + if self.direction == "read": + castedAccessExpr = "(%s)(%s)" % ("void*", access) + else: + castedAccessExpr = access + + sTypeAccess = self.exprAccessor(sTypeParam) + self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" % + self.rootTypeVarName) + self.cgen.stmt("%s = %s" % (self.rootTypeVarName, sTypeAccess)) + self.cgen.endIf() + + if self.direction == "read" and self.dynAlloc: + self.cgen.stmt("size_t %s" % sizeVar) + self.cgen.stmt("%s = %s->getBe32()" % \ + (sizeVar, self.streamVarName)) + self.cgen.stmt("%s = nullptr" % access) + self.cgen.beginIf(sizeVar) + self.cgen.stmt( \ + "%s->alloc((void**)&%s, sizeof(VkStructureType))" % + (self.streamVarName, access)) + + self.genStreamCall(vulkanType, access, "sizeof(VkStructureType)") + self.cgen.stmt("VkStructureType extType = *(VkStructureType*)(%s)" % access) + self.cgen.stmt( \ + "%s->alloc((void**)&%s, %s(%s->getFeatureBits(), %s, %s))" % + (self.streamVarName, access, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, self.streamVarName, self.rootTypeVarName, access)) + self.cgen.stmt("*(VkStructureType*)%s = extType" % access) + + self.cgen.funcCall(None, self.marshalPrefix + "extension_struct", + [self.streamVarName, self.rootTypeVarName, castedAccessExpr]) + self.cgen.endIf() + else: + + self.cgen.funcCall(None, self.marshalPrefix + "extension_struct", + [self.streamVarName, self.rootTypeVarName, castedAccessExpr]) + + + def onPointer(self, vulkanType): + access = self.exprAccessor(vulkanType) + + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.beginFilterGuard(vulkanType) + self.doAllocSpace(vulkanType) + + if vulkanType.filterVar != None: + print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + + if vulkanType.isHandleType() and self.mapHandles: + self.genHandleMappingCall(vulkanType, access, lenAccess) + else: + if self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + if lenAccess is not None: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t i = 0", "i < (uint32_t)%s" % lenAccess, "++i") + self.genPrimitiveStreamCall(vulkanType.getForValueAccess(), "%s[i]" % access) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + else: + self.genPrimitiveStreamCall(vulkanType.getForValueAccess(), "(*%s)" % access) + else: + if lenAccess is not None: + finalLenExpr = "%s * %s" % ( + lenAccess, self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + else: + finalLenExpr = "%s" % ( + self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + self.genStreamCall(vulkanType, access, finalLenExpr) + + if self.direction == "read": + self.endFilterGuard(vulkanType, "%s = 0" % access) + else: + self.endFilterGuard(vulkanType) + + def onValue(self, vulkanType): + self.beginFilterGuard(vulkanType) + + if vulkanType.isHandleType() and self.mapHandles: + access = self.exprAccessor(vulkanType) + if vulkanType.filterVar != None: + print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + self.genHandleMappingCall( + vulkanType.getForAddressAccess(), access, "1") + elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + access = self.exprPrimitiveValueAccessor(vulkanType) + self.genPrimitiveStreamCall(vulkanType, access) + else: + access = self.exprAccessor(vulkanType) + self.genStreamCall(vulkanType, access, self.cgen.sizeofExpr(vulkanType)) + + self.endFilterGuard(vulkanType) + + def streamLetParameter(self, structInfo, letParamInfo): + filterFeature = "%s->getFeatureBits() & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.streamVarName + self.cgen.stmt("%s %s = 1" % (letParamInfo.typeName, letParamInfo.paramName)) + + self.cgen.beginIf(filterFeature) + + if self.direction == "write": + bodyExpr = self.currentStructInfo.environment[letParamInfo.paramName]["body"] + self.cgen.stmt("%s = %s" % (letParamInfo.paramName, self.genFilterFunc(bodyExpr, self.currentStructInfo.environment))) + + self.genPrimitiveStreamCall(letParamInfo, letParamInfo.paramName) + + self.cgen.endIf() + + +class VulkanMarshaling(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo, variant="host"): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.cgenHeader = CodeGen() + self.cgenImpl = CodeGen() + + self.variant = variant + + self.currentFeature = None + self.apiOpcodes = {} + self.dynAlloc = self.variant != "guest" + + if self.variant == "guest": + self.marshalingParams = PARAMETERS_MARSHALING_GUEST + else: + self.marshalingParams = PARAMETERS_MARSHALING + + self.writeCodegen = \ + VulkanMarshalingCodegen( + None, + VULKAN_STREAM_VAR_NAME, + ROOT_TYPE_VAR_NAME, + MARSHAL_INPUT_VAR_NAME, + API_PREFIX_MARSHAL, + direction = "write") + + self.readCodegen = \ + VulkanMarshalingCodegen( + None, + VULKAN_STREAM_VAR_NAME, + ROOT_TYPE_VAR_NAME, + UNMARSHAL_INPUT_VAR_NAME, + API_PREFIX_UNMARSHAL, + direction = "read", + dynAlloc=self.dynAlloc) + + self.knownDefs = {} + + # Begin Vulkan API opcodes from something high + # that is not going to interfere with renderControl + # opcodes + self.beginOpcodeOld = 20000 + self.endOpcodeOld = 30000 + + self.beginOpcode = 200000000 + self.endOpcode = 300000000 + self.knownOpcodes = set() + + self.extensionMarshalPrototype = \ + VulkanAPI(API_PREFIX_MARSHAL + "extension_struct", + STREAM_RET_TYPE, + self.marshalingParams + + [STRUCT_EXTENSION_PARAM]) + + self.extensionUnmarshalPrototype = \ + VulkanAPI(API_PREFIX_UNMARSHAL + "extension_struct", + STREAM_RET_TYPE, + self.marshalingParams + + [STRUCT_EXTENSION_PARAM_FOR_WRITE]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.cgenImpl.makeFuncDecl(self.extensionMarshalPrototype)) + self.module.appendImpl(self.cgenImpl.makeFuncDecl(self.extensionUnmarshalPrototype)) + + def onBeginFeature(self, featureName, featureType): + VulkanWrapperGenerator.onBeginFeature(self, featureName, featureType) + self.currentFeature = featureName + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + self.module.appendHeader( + self.cgenHeader.makeFuncAlias(API_PREFIX_MARSHAL + name, + API_PREFIX_MARSHAL + alias)) + self.module.appendHeader( + self.cgenHeader.makeFuncAlias(API_PREFIX_UNMARSHAL + name, + API_PREFIX_UNMARSHAL + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + marshalParams = self.marshalingParams + \ + [makeVulkanTypeSimple(True, name, 1, MARSHAL_INPUT_VAR_NAME)] + + freeParams = [] + letParams = [] + + for (envname, bindingInfo) in list(sorted(structInfo.environment.items(), key = lambda kv: kv[0])): + if None == bindingInfo["binding"]: + freeParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + else: + if not bindingInfo["structmember"]: + letParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + + marshalPrototype = \ + VulkanAPI(API_PREFIX_MARSHAL + name, + STREAM_RET_TYPE, + marshalParams + freeParams) + + marshalPrototypeNoFilter = \ + VulkanAPI(API_PREFIX_MARSHAL + name, + STREAM_RET_TYPE, + marshalParams) + + def structMarshalingCustom(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + marshalingCode = \ + CUSTOM_MARSHAL_TYPES[name]["common"] + \ + CUSTOM_MARSHAL_TYPES[name]["marshaling"].format( + streamVarName=self.writeCodegen.streamVarName, + rootTypeVarName=self.writeCodegen.rootTypeVarName, + inputVarName=self.writeCodegen.inputVarName, + newInputVarName=self.writeCodegen.inputVarName + "_new") + for line in marshalingCode.split('\n'): + cgen.line(line) + + def structMarshalingDef(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.writeCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.writeCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.writeCodegen) + + def structMarshalingDefNoFilter(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + self.writeCodegen.doFiltering = False + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.writeCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.writeCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.writeCodegen) + self.writeCodegen.doFiltering = True + + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(marshalPrototype)) + + if name in CUSTOM_MARSHAL_TYPES: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototype, structMarshalingCustom)) + else: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototype, structMarshalingDef)) + + if freeParams != []: + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(marshalPrototypeNoFilter)) + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototypeNoFilter, structMarshalingDefNoFilter)) + + unmarshalPrototype = \ + VulkanAPI(API_PREFIX_UNMARSHAL + name, + STREAM_RET_TYPE, + self.marshalingParams + [makeVulkanTypeSimple(False, name, 1, UNMARSHAL_INPUT_VAR_NAME)] + freeParams) + + unmarshalPrototypeNoFilter = \ + VulkanAPI(API_PREFIX_UNMARSHAL + name, + STREAM_RET_TYPE, + self.marshalingParams + [makeVulkanTypeSimple(False, name, 1, UNMARSHAL_INPUT_VAR_NAME)]) + + def structUnmarshalingCustom(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + unmarshalingCode = \ + CUSTOM_MARSHAL_TYPES[name]["common"] + \ + CUSTOM_MARSHAL_TYPES[name]["unmarshaling"].format( + streamVarName=self.readCodegen.streamVarName, + rootTypeVarName=self.readCodegen.rootTypeVarName, + inputVarName=self.readCodegen.inputVarName, + newInputVarName=self.readCodegen.inputVarName + "_new") + for line in unmarshalingCode.split('\n'): + cgen.line(line) + + def structUnmarshalingDef(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # unmarshal 'let' parameters first + for letp in letParams: + self.readCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.readCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.readCodegen) + + def structUnmarshalingDefNoFilter(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + self.readCodegen.doFiltering = False + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # unmarshal 'let' parameters first + for letp in letParams: + iterateVulkanType(self.typeInfo, letp, self.readCodegen) + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.readCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.readCodegen) + self.readCodegen.doFiltering = True + + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(unmarshalPrototype)) + + if name in CUSTOM_MARSHAL_TYPES: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototype, structUnmarshalingCustom)) + else: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototype, structUnmarshalingDef)) + + if freeParams != []: + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(unmarshalPrototypeNoFilter)) + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototypeNoFilter, structUnmarshalingDefNoFilter)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + if name in KNOWN_FUNCTION_OPCODES: + opcode = KNOWN_FUNCTION_OPCODES[name] + else: + hashCode = hashlib.sha256(name.encode()).hexdigest()[:8] + hashInt = int(hashCode, 16) + opcode = self.beginOpcode + hashInt % (self.endOpcode - self.beginOpcode) + hasHashCollision = False + while opcode in self.knownOpcodes: + hasHashCollision = True + opcode += 1 + if hasHashCollision: + print("Hash collision occurred on function '{}'. " + "Please add the following line to marshalingdefs.py:".format(name), file=sys.stderr) + print("----------------------", file=sys.stderr) + print(" \"{}\": {},".format(name, opcode), file=sys.stderr) + print("----------------------", file=sys.stderr) + + self.module.appendHeader( + "#define OP_%s %d\n" % (name, opcode)) + self.apiOpcodes[name] = (opcode, self.currentFeature) + self.knownOpcodes.add(opcode) + + def doExtensionStructMarshalingCodegen(self, cgen, retType, extParam, forEach, funcproto, direction): + accessVar = "structAccess" + sizeVar = "currExtSize" + cgen.stmt("VkInstanceCreateInfo* %s = (VkInstanceCreateInfo*)(%s)" % (accessVar, extParam.paramName)) + cgen.stmt("size_t %s = %s(%s->getFeatureBits(), %s, %s)" % (sizeVar, + EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, extParam.paramName)) + + cgen.beginIf("!%s && %s" % (sizeVar, extParam.paramName)) + + cgen.line("// unknown struct extension; skip and call on its pNext field"); + cgen.funcCall(None, funcproto.name, [ + "vkStream", ROOT_TYPE_VAR_NAME, "(void*)%s->pNext" % accessVar]) + cgen.stmt("return") + + cgen.endIf() + cgen.beginElse() + + cgen.line("// known or null extension struct") + + if direction == "write": + cgen.stmt("vkStream->putBe32(%s)" % sizeVar) + elif not self.dynAlloc: + cgen.stmt("vkStream->getBe32()"); + + cgen.beginIf("!%s" % (sizeVar)) + cgen.line("// exit if this was a null extension struct (size == 0 in this branch)") + cgen.stmt("return") + cgen.endIf() + + cgen.endIf() + + # Now we can do stream stuff + if direction == "write": + cgen.stmt("vkStream->write(%s, sizeof(VkStructureType))" % extParam.paramName) + elif not self.dynAlloc: + cgen.stmt("uint64_t pNext_placeholder") + placeholderAccess = "(&pNext_placeholder)" + cgen.stmt("vkStream->read((void*)(&pNext_placeholder), sizeof(VkStructureType))") + cgen.stmt("(void)pNext_placeholder") + + def fatalDefault(cgen): + cgen.line("// fatal; the switch is only taken if the extension struct is known"); + cgen.stmt("abort()") + pass + + self.emitForEachStructExtension( + cgen, + retType, + extParam, + forEach, + defaultEmit=fatalDefault, + rootTypeVar=ROOT_TYPE_PARAM) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def forEachExtensionMarshal(ext, castedAccess, cgen): + cgen.funcCall(None, API_PREFIX_MARSHAL + ext.name, + [VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, castedAccess]) + + def forEachExtensionUnmarshal(ext, castedAccess, cgen): + cgen.funcCall(None, API_PREFIX_UNMARSHAL + ext.name, + [VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, castedAccess]) + + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + self.extensionMarshalPrototype, + lambda cgen: self.doExtensionStructMarshalingCodegen( + cgen, + STREAM_RET_TYPE, + STRUCT_EXTENSION_PARAM, + forEachExtensionMarshal, + self.extensionMarshalPrototype, + "write"))) + + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + self.extensionUnmarshalPrototype, + lambda cgen: self.doExtensionStructMarshalingCodegen( + cgen, + STREAM_RET_TYPE, + STRUCT_EXTENSION_PARAM_FOR_WRITE, + forEachExtensionUnmarshal, + self.extensionUnmarshalPrototype, + "read"))) + + opcode2stringPrototype = \ + VulkanAPI("api_opcode_to_string", + makeVulkanTypeSimple(True, "char", 1, "none"), + [ makeVulkanTypeSimple(True, "uint32_t", 0, "opcode") ]) + + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(opcode2stringPrototype)) + + def emitOpcode2StringImpl(apiOpcodes, cgen): + cgen.line("switch(opcode)") + cgen.beginBlock() + + currFeature = None + + for (name, (opcodeNum, feature)) in sorted(apiOpcodes.items(), key = lambda x : x[1][0]): + if not currFeature: + cgen.leftline("#ifdef %s" % feature) + currFeature = feature + + if currFeature and feature != currFeature: + cgen.leftline("#endif") + cgen.leftline("#ifdef %s" % feature) + currFeature = feature + + cgen.line("case OP_%s:" % name) + cgen.beginBlock() + cgen.stmt("return \"OP_%s\"" % name) + cgen.endBlock() + + if currFeature: + cgen.leftline("#endif") + + cgen.line("default:") + cgen.beginBlock() + cgen.stmt("return \"OP_UNKNOWN_API_CALL\"") + cgen.endBlock() + + cgen.endBlock() + + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + opcode2stringPrototype, + lambda cgen: emitOpcode2StringImpl(self.apiOpcodes, cgen))) + + self.module.appendHeader( + "#define OP_vkFirst_old %d\n" % (self.beginOpcodeOld)) + self.module.appendHeader( + "#define OP_vkLast_old %d\n" % (self.endOpcodeOld)) + self.module.appendHeader( + "#define OP_vkFirst %d\n" % (self.beginOpcode)) + self.module.appendHeader( + "#define OP_vkLast %d\n" % (self.endOpcode)) diff --git a/src/gfxstream/codegen/scripts/cereal/marshalingdefs.py b/src/gfxstream/codegen/scripts/cereal/marshalingdefs.py new file mode 100644 index 00000000000..88791f68b86 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/marshalingdefs.py @@ -0,0 +1,528 @@ +# Copyright (c) 2021 The Android Open Source Project +# Copyright (c) 2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +KNOWN_FUNCTION_OPCODES = { + "vkCreateInstance": 20000, + "vkDestroyInstance": 20001, + "vkEnumeratePhysicalDevices": 20002, + "vkGetPhysicalDeviceFeatures": 20003, + "vkGetPhysicalDeviceFormatProperties": 20004, + "vkGetPhysicalDeviceImageFormatProperties": 20005, + "vkGetPhysicalDeviceProperties": 20006, + "vkGetPhysicalDeviceQueueFamilyProperties": 20007, + "vkGetPhysicalDeviceMemoryProperties": 20008, + "vkGetInstanceProcAddr": 20009, + "vkGetDeviceProcAddr": 20010, + "vkCreateDevice": 20011, + "vkDestroyDevice": 20012, + "vkEnumerateInstanceExtensionProperties": 20013, + "vkEnumerateDeviceExtensionProperties": 20014, + "vkEnumerateInstanceLayerProperties": 20015, + "vkEnumerateDeviceLayerProperties": 20016, + "vkGetDeviceQueue": 20017, + "vkQueueSubmit": 20018, + "vkQueueWaitIdle": 20019, + "vkDeviceWaitIdle": 20020, + "vkAllocateMemory": 20021, + "vkFreeMemory": 20022, + "vkMapMemory": 20023, + "vkUnmapMemory": 20024, + "vkFlushMappedMemoryRanges": 20025, + "vkInvalidateMappedMemoryRanges": 20026, + "vkGetDeviceMemoryCommitment": 20027, + "vkBindBufferMemory": 20028, + "vkBindImageMemory": 20029, + "vkGetBufferMemoryRequirements": 20030, + "vkGetImageMemoryRequirements": 20031, + "vkGetImageSparseMemoryRequirements": 20032, + "vkGetPhysicalDeviceSparseImageFormatProperties": 20033, + "vkQueueBindSparse": 20034, + "vkCreateFence": 20035, + "vkDestroyFence": 20036, + "vkResetFences": 20037, + "vkGetFenceStatus": 20038, + "vkWaitForFences": 20039, + "vkCreateSemaphore": 20040, + "vkDestroySemaphore": 20041, + "vkCreateEvent": 20042, + "vkDestroyEvent": 20043, + "vkGetEventStatus": 20044, + "vkSetEvent": 20045, + "vkResetEvent": 20046, + "vkCreateQueryPool": 20047, + "vkDestroyQueryPool": 20048, + "vkGetQueryPoolResults": 20049, + "vkCreateBuffer": 20050, + "vkDestroyBuffer": 20051, + "vkCreateBufferView": 20052, + "vkDestroyBufferView": 20053, + "vkCreateImage": 20054, + "vkDestroyImage": 20055, + "vkGetImageSubresourceLayout": 20056, + "vkCreateImageView": 20057, + "vkDestroyImageView": 20058, + "vkCreateShaderModule": 20059, + "vkDestroyShaderModule": 20060, + "vkCreatePipelineCache": 20061, + "vkDestroyPipelineCache": 20062, + "vkGetPipelineCacheData": 20063, + "vkMergePipelineCaches": 20064, + "vkCreateGraphicsPipelines": 20065, + "vkCreateComputePipelines": 20066, + "vkDestroyPipeline": 20067, + "vkCreatePipelineLayout": 20068, + "vkDestroyPipelineLayout": 20069, + "vkCreateSampler": 20070, + "vkDestroySampler": 20071, + "vkCreateDescriptorSetLayout": 20072, + "vkDestroyDescriptorSetLayout": 20073, + "vkCreateDescriptorPool": 20074, + "vkDestroyDescriptorPool": 20075, + "vkResetDescriptorPool": 20076, + "vkAllocateDescriptorSets": 20077, + "vkFreeDescriptorSets": 20078, + "vkUpdateDescriptorSets": 20079, + "vkCreateFramebuffer": 20080, + "vkDestroyFramebuffer": 20081, + "vkCreateRenderPass": 20082, + "vkDestroyRenderPass": 20083, + "vkGetRenderAreaGranularity": 20084, + "vkCreateCommandPool": 20085, + "vkDestroyCommandPool": 20086, + "vkResetCommandPool": 20087, + "vkAllocateCommandBuffers": 20088, + "vkFreeCommandBuffers": 20089, + "vkBeginCommandBuffer": 20090, + "vkEndCommandBuffer": 20091, + "vkResetCommandBuffer": 20092, + "vkCmdBindPipeline": 20093, + "vkCmdSetViewport": 20094, + "vkCmdSetScissor": 20095, + "vkCmdSetLineWidth": 20096, + "vkCmdSetDepthBias": 20097, + "vkCmdSetBlendConstants": 20098, + "vkCmdSetDepthBounds": 20099, + "vkCmdSetStencilCompareMask": 20100, + "vkCmdSetStencilWriteMask": 20101, + "vkCmdSetStencilReference": 20102, + "vkCmdBindDescriptorSets": 20103, + "vkCmdBindIndexBuffer": 20104, + "vkCmdBindVertexBuffers": 20105, + "vkCmdDraw": 20106, + "vkCmdDrawIndexed": 20107, + "vkCmdDrawIndirect": 20108, + "vkCmdDrawIndexedIndirect": 20109, + "vkCmdDispatch": 20110, + "vkCmdDispatchIndirect": 20111, + "vkCmdCopyBuffer": 20112, + "vkCmdCopyImage": 20113, + "vkCmdBlitImage": 20114, + "vkCmdCopyBufferToImage": 20115, + "vkCmdCopyImageToBuffer": 20116, + "vkCmdUpdateBuffer": 20117, + "vkCmdFillBuffer": 20118, + "vkCmdClearColorImage": 20119, + "vkCmdClearDepthStencilImage": 20120, + "vkCmdClearAttachments": 20121, + "vkCmdResolveImage": 20122, + "vkCmdSetEvent": 20123, + "vkCmdResetEvent": 20124, + "vkCmdWaitEvents": 20125, + "vkCmdPipelineBarrier": 20126, + "vkCmdBeginQuery": 20127, + "vkCmdEndQuery": 20128, + "vkCmdResetQueryPool": 20129, + "vkCmdWriteTimestamp": 20130, + "vkCmdCopyQueryPoolResults": 20131, + "vkCmdPushConstants": 20132, + "vkCmdBeginRenderPass": 20133, + "vkCmdNextSubpass": 20134, + "vkCmdEndRenderPass": 20135, + "vkCmdExecuteCommands": 20136, + "vkEnumerateInstanceVersion": 20137, + "vkBindBufferMemory2": 20138, + "vkBindImageMemory2": 20139, + "vkGetDeviceGroupPeerMemoryFeatures": 20140, + "vkCmdSetDeviceMask": 20141, + "vkCmdDispatchBase": 20142, + "vkEnumeratePhysicalDeviceGroups": 20143, + "vkGetImageMemoryRequirements2": 20144, + "vkGetBufferMemoryRequirements2": 20145, + "vkGetImageSparseMemoryRequirements2": 20146, + "vkGetPhysicalDeviceFeatures2": 20147, + "vkGetPhysicalDeviceProperties2": 20148, + "vkGetPhysicalDeviceFormatProperties2": 20149, + "vkGetPhysicalDeviceImageFormatProperties2": 20150, + "vkGetPhysicalDeviceQueueFamilyProperties2": 20151, + "vkGetPhysicalDeviceMemoryProperties2": 20152, + "vkGetPhysicalDeviceSparseImageFormatProperties2": 20153, + "vkTrimCommandPool": 20154, + "vkGetDeviceQueue2": 20155, + "vkCreateSamplerYcbcrConversion": 20156, + "vkDestroySamplerYcbcrConversion": 20157, + "vkCreateDescriptorUpdateTemplate": 20158, + "vkDestroyDescriptorUpdateTemplate": 20159, + "vkUpdateDescriptorSetWithTemplate": 20160, + "vkGetPhysicalDeviceExternalBufferProperties": 20161, + "vkGetPhysicalDeviceExternalFenceProperties": 20162, + "vkGetPhysicalDeviceExternalSemaphoreProperties": 20163, + "vkGetDescriptorSetLayoutSupport": 20164, + "vkDestroySurfaceKHR": 20165, + "vkGetPhysicalDeviceSurfaceSupportKHR": 20166, + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR": 20167, + "vkGetPhysicalDeviceSurfaceFormatsKHR": 20168, + "vkGetPhysicalDeviceSurfacePresentModesKHR": 20169, + "vkCreateSwapchainKHR": 20170, + "vkDestroySwapchainKHR": 20171, + "vkGetSwapchainImagesKHR": 20172, + "vkAcquireNextImageKHR": 20173, + "vkQueuePresentKHR": 20174, + "vkGetDeviceGroupPresentCapabilitiesKHR": 20175, + "vkGetDeviceGroupSurfacePresentModesKHR": 20176, + "vkGetPhysicalDevicePresentRectanglesKHR": 20177, + "vkAcquireNextImage2KHR": 20178, + "vkGetPhysicalDeviceDisplayPropertiesKHR": 20179, + "vkGetPhysicalDeviceDisplayPlanePropertiesKHR": 20180, + "vkGetDisplayPlaneSupportedDisplaysKHR": 20181, + "vkGetDisplayModePropertiesKHR": 20182, + "vkCreateDisplayModeKHR": 20183, + "vkGetDisplayPlaneCapabilitiesKHR": 20184, + "vkCreateDisplayPlaneSurfaceKHR": 20185, + "vkCreateSharedSwapchainsKHR": 20186, + "vkCreateXlibSurfaceKHR": 20187, + "vkGetPhysicalDeviceXlibPresentationSupportKHR": 20188, + "vkCreateXcbSurfaceKHR": 20189, + "vkGetPhysicalDeviceXcbPresentationSupportKHR": 20190, + "vkCreateWaylandSurfaceKHR": 20191, + "vkGetPhysicalDeviceWaylandPresentationSupportKHR": 20192, + "vkCreateMirSurfaceKHR": 20193, + "vkGetPhysicalDeviceMirPresentationSupportKHR": 20194, + "vkCreateAndroidSurfaceKHR": 20195, + "vkCreateWin32SurfaceKHR": 20196, + "vkGetPhysicalDeviceWin32PresentationSupportKHR": 20197, + "vkGetPhysicalDeviceFeatures2KHR": 20198, + "vkGetPhysicalDeviceProperties2KHR": 20199, + "vkGetPhysicalDeviceFormatProperties2KHR": 20200, + "vkGetPhysicalDeviceImageFormatProperties2KHR": 20201, + "vkGetPhysicalDeviceQueueFamilyProperties2KHR": 20202, + "vkGetPhysicalDeviceMemoryProperties2KHR": 20203, + "vkGetPhysicalDeviceSparseImageFormatProperties2KHR": 20204, + "vkGetDeviceGroupPeerMemoryFeaturesKHR": 20205, + "vkCmdSetDeviceMaskKHR": 20206, + "vkCmdDispatchBaseKHR": 20207, + "vkTrimCommandPoolKHR": 20208, + "vkEnumeratePhysicalDeviceGroupsKHR": 20209, + "vkGetPhysicalDeviceExternalBufferPropertiesKHR": 20210, + "vkGetMemoryWin32HandleKHR": 20211, + "vkGetMemoryWin32HandlePropertiesKHR": 20212, + "vkGetMemoryFdKHR": 20213, + "vkGetMemoryFdPropertiesKHR": 20214, + "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR": 20215, + "vkImportSemaphoreWin32HandleKHR": 20216, + "vkGetSemaphoreWin32HandleKHR": 20217, + "vkImportSemaphoreFdKHR": 20218, + "vkGetSemaphoreFdKHR": 20219, + "vkCmdPushDescriptorSetKHR": 20220, + "vkCmdPushDescriptorSetWithTemplateKHR": 20221, + "vkCreateDescriptorUpdateTemplateKHR": 20222, + "vkDestroyDescriptorUpdateTemplateKHR": 20223, + "vkUpdateDescriptorSetWithTemplateKHR": 20224, + "vkCreateRenderPass2KHR": 20225, + "vkCmdBeginRenderPass2KHR": 20226, + "vkCmdNextSubpass2KHR": 20227, + "vkCmdEndRenderPass2KHR": 20228, + "vkGetSwapchainStatusKHR": 20229, + "vkGetPhysicalDeviceExternalFencePropertiesKHR": 20230, + "vkImportFenceWin32HandleKHR": 20231, + "vkGetFenceWin32HandleKHR": 20232, + "vkImportFenceFdKHR": 20233, + "vkGetFenceFdKHR": 20234, + "vkGetPhysicalDeviceSurfaceCapabilities2KHR": 20235, + "vkGetPhysicalDeviceSurfaceFormats2KHR": 20236, + "vkGetPhysicalDeviceDisplayProperties2KHR": 20237, + "vkGetPhysicalDeviceDisplayPlaneProperties2KHR": 20238, + "vkGetDisplayModeProperties2KHR": 20239, + "vkGetDisplayPlaneCapabilities2KHR": 20240, + "vkGetImageMemoryRequirements2KHR": 20241, + "vkGetBufferMemoryRequirements2KHR": 20242, + "vkGetImageSparseMemoryRequirements2KHR": 20243, + "vkCreateSamplerYcbcrConversionKHR": 20244, + "vkDestroySamplerYcbcrConversionKHR": 20245, + "vkBindBufferMemory2KHR": 20246, + "vkBindImageMemory2KHR": 20247, + "vkGetDescriptorSetLayoutSupportKHR": 20248, + "vkCmdDrawIndirectCountKHR": 20249, + "vkCmdDrawIndexedIndirectCountKHR": 20250, + "vkGetSwapchainGrallocUsageANDROID": 20251, + "vkAcquireImageANDROID": 20252, + "vkQueueSignalReleaseImageANDROID": 20253, + "vkCreateDebugReportCallbackEXT": 20254, + "vkDestroyDebugReportCallbackEXT": 20255, + "vkDebugReportMessageEXT": 20256, + "vkDebugMarkerSetObjectTagEXT": 20257, + "vkDebugMarkerSetObjectNameEXT": 20258, + "vkCmdDebugMarkerBeginEXT": 20259, + "vkCmdDebugMarkerEndEXT": 20260, + "vkCmdDebugMarkerInsertEXT": 20261, + "vkCmdDrawIndirectCountAMD": 20262, + "vkCmdDrawIndexedIndirectCountAMD": 20263, + "vkGetShaderInfoAMD": 20264, + "vkGetPhysicalDeviceExternalImageFormatPropertiesNV": 20265, + "vkGetMemoryWin32HandleNV": 20266, + "vkCreateViSurfaceNN": 20267, + "vkCmdBeginConditionalRenderingEXT": 20268, + "vkCmdEndConditionalRenderingEXT": 20269, + "vkCmdProcessCommandsNVX": 20270, + "vkCmdReserveSpaceForCommandsNVX": 20271, + "vkCreateIndirectCommandsLayoutNVX": 20272, + "vkDestroyIndirectCommandsLayoutNVX": 20273, + "vkCreateObjectTableNVX": 20274, + "vkDestroyObjectTableNVX": 20275, + "vkRegisterObjectsNVX": 20276, + "vkUnregisterObjectsNVX": 20277, + "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX": 20278, + "vkCmdSetViewportWScalingNV": 20279, + "vkReleaseDisplayEXT": 20280, + "vkAcquireXlibDisplayEXT": 20281, + "vkGetRandROutputDisplayEXT": 20282, + "vkGetPhysicalDeviceSurfaceCapabilities2EXT": 20283, + "vkDisplayPowerControlEXT": 20284, + "vkRegisterDeviceEventEXT": 20285, + "vkRegisterDisplayEventEXT": 20286, + "vkGetSwapchainCounterEXT": 20287, + "vkGetRefreshCycleDurationGOOGLE": 20288, + "vkGetPastPresentationTimingGOOGLE": 20289, + "vkCmdSetDiscardRectangleEXT": 20290, + "vkSetHdrMetadataEXT": 20291, + "vkCreateIOSSurfaceMVK": 20292, + "vkCreateMacOSSurfaceMVK": 20293, + "vkSetDebugUtilsObjectNameEXT": 20294, + "vkSetDebugUtilsObjectTagEXT": 20295, + "vkQueueBeginDebugUtilsLabelEXT": 20296, + "vkQueueEndDebugUtilsLabelEXT": 20297, + "vkQueueInsertDebugUtilsLabelEXT": 20298, + "vkCmdBeginDebugUtilsLabelEXT": 20299, + "vkCmdEndDebugUtilsLabelEXT": 20300, + "vkCmdInsertDebugUtilsLabelEXT": 20301, + "vkCreateDebugUtilsMessengerEXT": 20302, + "vkDestroyDebugUtilsMessengerEXT": 20303, + "vkSubmitDebugUtilsMessageEXT": 20304, + "vkGetAndroidHardwareBufferPropertiesANDROID": 20305, + "vkGetMemoryAndroidHardwareBufferANDROID": 20306, + "vkCmdSetSampleLocationsEXT": 20307, + "vkGetPhysicalDeviceMultisamplePropertiesEXT": 20308, + "vkCreateValidationCacheEXT": 20309, + "vkDestroyValidationCacheEXT": 20310, + "vkMergeValidationCachesEXT": 20311, + "vkGetValidationCacheDataEXT": 20312, + "vkGetMemoryHostPointerPropertiesEXT": 20313, + "vkCmdWriteBufferMarkerAMD": 20314, + "vkCmdSetCheckpointNV": 20315, + "vkGetQueueCheckpointDataNV": 20316, + "vkMapMemoryIntoAddressSpaceGOOGLE": 20317, + "vkUpdateDescriptorSetWithTemplateSizedGOOGLE": 20320, + "vkBeginCommandBufferAsyncGOOGLE": 20321, + "vkEndCommandBufferAsyncGOOGLE": 20322, + "vkResetCommandBufferAsyncGOOGLE": 20323, + "vkCommandBufferHostSyncGOOGLE": 20324, + "vkCreateImageWithRequirementsGOOGLE": 20325, + "vkCreateBufferWithRequirementsGOOGLE": 20326, + "vkGetMemoryHostAddressInfoGOOGLE": 20327, + "vkFreeMemorySyncGOOGLE": 20328, + "vkQueueHostSyncGOOGLE": 20329, + "vkQueueSubmitAsyncGOOGLE": 20330, + "vkQueueWaitIdleAsyncGOOGLE": 20331, + "vkQueueBindSparseAsyncGOOGLE": 20332, + "vkGetLinearImageLayoutGOOGLE": 20333, + "vkGetMTLDeviceMVK": 20334, + "vkSetMTLTextureMVK": 20335, + "vkGetMTLTextureMVK": 20336, + "vkGetMTLBufferMVK": 20337, + "vkUseIOSurfaceMVK": 20338, + "vkGetIOSurfaceMVK": 20339, + "vkQueueFlushCommandsGOOGLE": 20340, + "vkGetBlobGOOGLE": 20341, +} + +CUSTOM_MARSHAL_TYPES = { + "VkAccelerationStructureInstanceKHR": { + "common": """ +typedef struct VkAccelerationStructureInstanceKHRWithoutBitFields { + VkTransformMatrixKHR transform; + uint32_t dwords[2]; + uint64_t accelerationStructureReference; +} VkAccelerationStructureInstanceKHRWithoutBitFields; +""", + "marshaling": """ +const VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName}); +marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "unmarshaling": """ +VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName}); +unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "reservedmarshaling": """ +(void)vkStream; +const VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName}); +reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + "reservedunmarshaling": """ +VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName}); +reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + }, + "VkAccelerationStructureMatrixMotionInstanceNV": { + "common": """ +typedef struct VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields { + VkTransformMatrixKHR transformT0; + VkTransformMatrixKHR transformT1; + uint32_t dwords[2]; + uint64_t accelerationStructureReference; +} VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields; +""", + "marshaling": """ +const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName}); +marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0)); +marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "unmarshaling": """ +VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName}); +unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0)); +unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "reservedmarshaling": """ +(void)vkStream; +const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName}); +reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0), ptr); +reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + "reservedunmarshaling": """ +VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName}); +reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0), ptr); +reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + }, + "VkAccelerationStructureSRTMotionInstanceNV": { + "common": """ +typedef struct VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields { + VkSRTDataNV transformT0; + VkSRTDataNV transformT1; + uint32_t dwords[2]; + uint64_t accelerationStructureReference; +} VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields; +""", + "marshaling": """ +const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName}); +marshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0)); +marshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "unmarshaling": """ +VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName}); +unmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0)); +unmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1)); +for (uint32_t i = 0; i < 2; i++) {{ + {streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); +}} +{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +""", + "reservedmarshaling": """ +(void)vkStream; +const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName}); +reservedmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0), ptr); +reservedmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + "reservedunmarshaling": """ +VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName}); +reservedunmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0), ptr); +reservedunmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1), ptr); +for (uint32_t i = 0; i < 2; i++) {{ + memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t)); + *ptr += sizeof(uint32_t); +}} +memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t)); +*ptr += sizeof(uint64_t); +""", + }, + "VkXcbSurfaceCreateInfoKHR": { + "common": """ +// This struct should never be marshaled / unmarshaled. +__builtin_trap(); +""", + "marshaling": "", + "unmarshaling": "", + "reservedmarshaling": "", + "reservedunmarshaling": "", + }, + "VkMetalSurfaceCreateInfoEXT": { + "common": """ +// This struct should never be marshaled / unmarshaled. +__builtin_trap(); +""", + "marshaling": "", + "unmarshaling": "", + "reservedmarshaling": "", + "reservedunmarshaling": "", + }, +} diff --git a/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py b/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py new file mode 100644 index 00000000000..3b54a4133a3 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/reservedmarshaling.py @@ -0,0 +1,1061 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from copy import copy + +from .common.codegen import CodeGen, VulkanAPIWrapper +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator, Atom, FuncExpr, FuncExprVal, FuncLambda + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import VULKAN_STREAM_VAR_NAME +from .wrapperdefs import ROOT_TYPE_VAR_NAME, ROOT_TYPE_PARAM +from .wrapperdefs import STREAM_RET_TYPE +from .wrapperdefs import MARSHAL_INPUT_VAR_NAME +from .wrapperdefs import UNMARSHAL_INPUT_VAR_NAME +from .wrapperdefs import PARAMETERS_MARSHALING +from .wrapperdefs import PARAMETERS_MARSHALING_GUEST +from .wrapperdefs import STYPE_OVERRIDE +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME +from .wrapperdefs import API_PREFIX_RESERVEDMARSHAL +from .wrapperdefs import API_PREFIX_RESERVEDUNMARSHAL + +from .marshalingdefs import CUSTOM_MARSHAL_TYPES +class VulkanReservedMarshalingCodegen(VulkanTypeIterator): + def __init__(self, + cgen, + variant, + streamVarName, + rootTypeVarName, + inputVarName, + ptrVarName, + marshalPrefix, + handlemapPrefix, + direction = "write", + forApiOutput = False, + dynAlloc = False, + mapHandles = True, + handleMapOverwrites = False, + doFiltering = True, + stackVar=None, + stackArrSize=None): + self.cgen = cgen + self.variant = variant + self.direction = direction + self.processSimple = "write" if self.direction == "write" else "read" + self.forApiOutput = forApiOutput + + self.checked = False + + self.streamVarName = streamVarName + self.rootTypeVarName = rootTypeVarName + self.inputVarName = inputVarName + self.ptrVar = ptrVarName + self.marshalPrefix = marshalPrefix + self.handlemapPrefix = handlemapPrefix + + self.exprAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = True) + self.exprValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = False) + self.exprPrimitiveValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.inputVarName, asPtr = False) + self.lenAccessor = lambda t: self.cgen.generalLengthAccess(t, parentVarName = self.inputVarName) + self.lenAccessorGuard = lambda t: self.cgen.generalLengthAccessGuard( + t, parentVarName=self.inputVarName) + self.filterVarAccessor = lambda t: self.cgen.filterVarAccess(t, parentVarName = self.inputVarName) + + self.dynAlloc = dynAlloc + self.mapHandles = mapHandles + self.handleMapOverwrites = handleMapOverwrites + self.doFiltering = doFiltering + + self.stackVar = stackVar + self.stackArrSize = stackArrSize + + def getTypeForStreaming(self, vulkanType): + res = copy(vulkanType) + + if not vulkanType.accessibleAsPointer(): + res = res.getForAddressAccess() + + if vulkanType.staticArrExpr: + res = res.getForAddressAccess() + + if self.direction == "write": + return res + else: + return res.getForNonConstAccess() + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def genPtrIncr(self, sizeExpr): + self.cgen.stmt("*%s += %s" % (self.ptrVar, sizeExpr)) + + def genMemcpyAndIncr(self, varname, cast, toStreamExpr, sizeExpr, toBe = False, actualSize = 4): + if self.direction == "write": + self.cgen.stmt("memcpy(*%s, %s%s, %s)" % (varname, cast, toStreamExpr, sizeExpr)) + else: + self.cgen.stmt("memcpy(%s%s, *%s, %s)" % (cast, toStreamExpr, varname, sizeExpr)) + + if toBe: + streamPrefix = "to" + if "read" == self.direction: + streamPrefix = "from" + + streamMethod = streamPrefix + + if 1 == actualSize: + streamMethod += "Byte" + elif 2 == actualSize: + streamMethod += "Be16" + elif 4 == actualSize: + streamMethod += "Be32" + elif 8 == actualSize: + streamMethod += "Be64" + else: + pass + + streamNamespace = "gfxstream::guest" if self.variant == "guest" else "android::base" + if self.direction == "write": + self.cgen.stmt("%s::Stream::%s((uint8_t*)*%s)" % (streamNamespace, streamMethod, varname)) + else: + self.cgen.stmt("%s::Stream::%s((uint8_t*)%s)" % (streamNamespace, streamMethod, toStreamExpr)) + + self.genPtrIncr(sizeExpr) + + def genStreamCall(self, vulkanType, toStreamExpr, sizeExpr): + varname = self.ptrVar + cast = self.makeCastExpr(self.getTypeForStreaming(vulkanType)) + self.genMemcpyAndIncr(varname, cast, toStreamExpr, sizeExpr) + + def genPrimitiveStreamCall(self, vulkanType, access): + varname = self.ptrVar + self.cgen.memcpyPrimitive( + self.typeInfo, + "(*" + varname + ")", + access, + vulkanType, + self.variant, + direction=self.direction) + self.genPtrIncr(str(self.cgen.countPrimitive( + self.typeInfo, + vulkanType))) + + def genHandleMappingCall(self, vulkanType, access, lenAccess, lenAccessGuard): + + if lenAccess is None: + lenAccess = "1" + handle64Bytes = "8" + else: + handle64Bytes = "%s * 8" % lenAccess + + handle64Var = self.cgen.var() + if lenAccess != "1": + self.cgen.beginIf(lenAccess) + self.cgen.stmt("uint8_t* %s_ptr = (uint8_t*)(*%s)" % (handle64Var, self.ptrVar)) + handle64VarAccess = handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 1, paramName=handle64Var) + else: + self.cgen.stmt("uint64_t %s" % handle64Var) + handle64VarAccess = "&%s" % handle64Var + handle64VarType = \ + makeVulkanTypeSimple(False, "uint64_t", 0, paramName=handle64Var) + + if "" == self.handlemapPrefix: + mapFunc = ("(%s)" % vulkanType.typeName) + mapFunc64 = ("(%s)" % "uint64_t") + else: + mapFunc = self.handlemapPrefix + vulkanType.typeName + mapFunc64 = mapFunc + + if self.direction == "write": + if self.handleMapOverwrites: + self.cgen.stmt( + "static_assert(8 == sizeof(%s), \"handle map overwrite requres %s to be 8 bytes long\")" % \ + (vulkanType.typeName, vulkanType.typeName)) + if "1" == lenAccess: + self.cgen.stmt("*%s = (%s)%s(*%s)" % (access, vulkanType.typeName, mapFunc, access)) + self.genStreamCall(vulkanType, access, "8 * %s" % lenAccess) + else: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t k = 0", "k < %s" % lenAccess, "++k") + self.cgen.stmt("%s[k] = (%s)%s(%s[k])" % (access, vulkanType.typeName, mapFunc, access)) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + self.genPtrIncr("8 * %s" % lenAccess) + else: + if "1" == lenAccess: + self.cgen.stmt("*%s = %s((*%s))" % (handle64VarAccess, mapFunc64, access)) + self.genStreamCall(handle64VarType, handle64VarAccess, handle64Bytes) + else: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t k = 0", "k < %s" % lenAccess, "++k") + self.cgen.stmt("uint64_t tmpval = %s(%s[k])" % (mapFunc64, access)) + self.cgen.stmt("memcpy(%s_ptr + k * 8, &tmpval, sizeof(uint64_t))" % (handle64Var)) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + self.genPtrIncr("8 * %s" % lenAccess) + else: + if "1" == lenAccess: + self.genStreamCall(handle64VarType, handle64VarAccess, handle64Bytes) + self.cgen.stmt("*%s%s = (%s)%s((%s)(*%s))" % ( + self.makeCastExpr(vulkanType.getForNonConstAccess()), access, + vulkanType.typeName, mapFunc, vulkanType.typeName, handle64VarAccess)) + else: + self.genPtrIncr("8 * %s" % lenAccess) + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t k = 0", "k < %s" % lenAccess, "++k") + self.cgen.stmt("uint64_t tmpval; memcpy(&tmpval, %s_ptr + k * 8, sizeof(uint64_t))" % handle64Var) + self.cgen.stmt("*((%s%s) + k) = (%s)%s((%s)tmpval)" % ( + self.makeCastExpr(vulkanType.getForNonConstAccess()), access, + vulkanType.typeName, mapFunc, vulkanType.typeName)) + if lenAccessGuard is not None: + self.cgen.endIf() + self.cgen.endFor() + + if lenAccess != "1": + self.cgen.endIf() + + def doAllocSpace(self, vulkanType): + if self.dynAlloc and self.direction == "read": + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + sizeof = self.cgen.sizeofExpr(vulkanType.getForValueAccess()) + if lenAccess: + bytesExpr = "%s * %s" % (lenAccess, sizeof) + else: + bytesExpr = sizeof + lenAccess = "1" + + if self.stackVar: + if self.stackArrSize != lenAccess: + self.cgen.beginIf("%s <= %s" % (lenAccess, self.stackArrSize)) + + self.cgen.stmt( + "%s = %s%s" % (access, self.makeCastExpr(vulkanType.getForNonConstAccess()), self.stackVar)) + + if self.stackArrSize != lenAccess: + self.cgen.endIf() + self.cgen.beginElse() + + if self.stackArrSize != lenAccess: + self.cgen.stmt( + "%s->alloc((void**)&%s, %s)" % + (self.streamVarName, + access, bytesExpr)) + + if self.stackArrSize != lenAccess: + self.cgen.endIf() + else: + self.cgen.stmt( + "%s->alloc((void**)&%s, %s)" % + (self.streamVarName, + access, bytesExpr)) + + def getOptionalStringFeatureExpr(self, vulkanType): + streamFeature = vulkanType.getProtectStreamFeature() + if streamFeature is None: + return None + return "%s->getFeatureBits() & %s" % (self.streamVarName, streamFeature) + + def onCheck(self, vulkanType): + if self.forApiOutput: + return + + featureExpr = self.getOptionalStringFeatureExpr(vulkanType) + self.checked = True + access = self.exprAccessor(vulkanType) + needConsistencyCheck = False + + self.cgen.line("// WARNING PTR CHECK") + if (self.dynAlloc and self.direction == "read") or self.direction == "write": + checkAccess = self.exprAccessor(vulkanType) + addrExpr = "&" + checkAccess + sizeExpr = self.cgen.sizeofExpr(vulkanType) + else: + checkName = "check_%s" % vulkanType.paramName + self.cgen.stmt("%s %s" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName = False), checkName)) + checkAccess = checkName + addrExpr = "&" + checkAccess + sizeExpr = self.cgen.sizeofExpr(vulkanType) + needConsistencyCheck = True + + if featureExpr is not None: + self.cgen.beginIf(featureExpr) + + self.genPrimitiveStreamCall( + vulkanType, + checkAccess) + + if featureExpr is not None: + self.cgen.endIf() + + if featureExpr is not None: + self.cgen.beginIf("(!(%s) || %s)" % (featureExpr, access)) + else: + self.cgen.beginIf(access) + + if needConsistencyCheck and featureExpr is None: + self.cgen.beginIf("!(%s)" % checkName) + self.cgen.stmt( + "fprintf(stderr, \"fatal: %s inconsistent between guest and host\\n\")" % (access)) + self.cgen.endIf() + + def onCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.beginIf("%s->getFeatureBits() & VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT" % self.streamVarName) + self.onCheck(vulkanType) + + def endCheckWithNullOptionalStringFeature(self, vulkanType): + self.endCheck(vulkanType) + self.cgen.endIf() + self.cgen.beginElse() + + def finalCheckWithNullOptionalStringFeature(self, vulkanType): + self.cgen.endElse() + + def endCheck(self, vulkanType): + + if self.checked: + self.cgen.endIf() + self.checked = False + + def genFilterFunc(self, filterfunc, env): + + def loop(expr, lambdaEnv={}): + def do_func(expr): + fnamestr = expr.name.name + if "not" == fnamestr: + return "!(%s)" % (loop(expr.args[0], lambdaEnv)) + if "eq" == fnamestr: + return "(%s == %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "and" == fnamestr: + return "(%s && %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "or" == fnamestr: + return "(%s || %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "bitwise_and" == fnamestr: + return "(%s & %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv)) + if "getfield" == fnamestr: + ptrlevels = get_ptrlevels(expr.args[0].val.name) + if ptrlevels == 0: + return "%s.%s" % (loop(expr.args[0], lambdaEnv), expr.args[1].val) + else: + return "(%s(%s)).%s" % ("*" * ptrlevels, loop(expr.args[0], lambdaEnv), expr.args[1].val) + + if "if" == fnamestr: + return "((%s) ? (%s) : (%s))" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv), loop(expr.args[2], lambdaEnv)) + + return "%s(%s)" % (fnamestr, ", ".join(map(lambda e: loop(e, lambdaEnv), expr.args))) + + def do_expratom(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return atomname + + enventry = env.get(atomname, None) + if None != enventry: + return self.getEnvAccessExpr(atomname) + return atomname + + def get_ptrlevels(atomname, lambdaEnv= {}): + if lambdaEnv.get(atomname, None) is not None: + return 0 + + enventry = env.get(atomname, None) + if None != enventry: + return self.getPointerIndirectionLevels(atomname) + + return 0 + + def do_exprval(expr, lambdaEnv= {}): + expratom = expr.val + + if Atom == type(expratom): + return do_expratom(expratom.name, lambdaEnv) + + return "%s" % expratom + + def do_lambda(expr, lambdaEnv= {}): + params = expr.vs + body = expr.body + newEnv = {} + + for (k, v) in lambdaEnv.items(): + newEnv[k] = v + + for p in params: + newEnv[p.name] = p.typ + + return "[](%s) { return %s; }" % (", ".join(list(map(lambda p: "%s %s" % (p.typ, p.name), params))), loop(body, lambdaEnv=newEnv)) + + if FuncExpr == type(expr): + return do_func(expr) + if FuncLambda == type(expr): + return do_lambda(expr) + elif FuncExprVal == type(expr): + return do_exprval(expr) + + return loop(filterfunc) + + def beginFilterGuard(self, vulkanType): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + filterVarAccess = self.getEnvAccessExpr(vulkanType.filterVar) + + filterValsExpr = None + filterFuncExpr = None + filterExpr = None + + filterFeature = "%s->getFeatureBits() & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.streamVarName + + if None != vulkanType.filterVals: + filterValsExpr = " || ".join(map(lambda filterval: "(%s == %s)" % (filterval, filterVarAccess), vulkanType.filterVals)) + + if None != vulkanType.filterFunc: + filterFuncExpr = self.genFilterFunc(vulkanType.filterFunc, self.currentStructInfo.environment) + + if None != filterValsExpr and None != filterFuncExpr: + filterExpr = "%s || %s" % (filterValsExpr, filterFuncExpr) + elif None == filterValsExpr and None == filterFuncExpr: + # Assume is bool + self.cgen.beginIf(filterVarAccess) + elif None != filterValsExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterValsExpr)) + elif None != filterFuncExpr: + self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterFuncExpr)) + + def endFilterGuard(self, vulkanType, cleanupExpr=None): + if vulkanType.filterVar == None: + return + + if self.doFiltering == False: + return + + if cleanupExpr == None: + self.cgen.endIf() + else: + self.cgen.endIf() + self.cgen.beginElse() + self.cgen.stmt(cleanupExpr) + self.cgen.endElse() + + def getEnvAccessExpr(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + envAccess = self.exprValueAccessor(list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0]) + else: + envAccess = varName + return envAccess + + return None + + def getPointerIndirectionLevels(self, varName): + parentEnvEntry = self.currentStructInfo.environment.get(varName, None) + + if parentEnvEntry != None: + isParentMember = parentEnvEntry["structmember"] + + if isParentMember: + return list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0].pointerIndirectionLevels + else: + return 0 + return 0 + + return 0 + + + def onCompoundType(self, vulkanType): + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + + self.beginFilterGuard(vulkanType) + + if vulkanType.pointerIndirectionLevels > 0: + self.doAllocSpace(vulkanType) + + if lenAccess is not None: + loopVar = "i" + access = "%s + %s" % (access, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess) + forIncr = "++%s" % loopVar + self.cgen.beginFor(forInit, forCond, forIncr) + + accessWithCast = "%s(%s)" % (self.makeCastExpr( + self.getTypeForStreaming(vulkanType)), access) + + callParams = [self.streamVarName, self.rootTypeVarName, accessWithCast, self.ptrVar] + + for (bindName, localName) in vulkanType.binds.items(): + callParams.append(self.getEnvAccessExpr(localName)) + + self.cgen.funcCall(None, self.marshalPrefix + vulkanType.typeName, + callParams) + + if lenAccess is not None: + self.cgen.endFor() + + if self.direction == "read": + self.endFilterGuard(vulkanType, "%s = 0" % self.exprAccessor(vulkanType)) + else: + self.endFilterGuard(vulkanType) + + def onString(self, vulkanType): + access = self.exprAccessor(vulkanType) + + if self.direction == "write": + self.cgen.beginBlock() + self.cgen.stmt("uint32_t l = %s ? strlen(%s): 0" % (access, access)) + self.genMemcpyAndIncr(self.ptrVar, "(uint32_t*)" ,"&l", "sizeof(uint32_t)", toBe = True, actualSize = 4) + self.genMemcpyAndIncr(self.ptrVar, "(char*)", access, "l") + self.cgen.endBlock() + else: + castExpr = \ + self.makeCastExpr( \ + self.getTypeForStreaming( \ + vulkanType.getForAddressAccess())) + self.cgen.stmt( \ + "%s->loadStringInPlaceWithStreamPtr(%s&%s, %s)" % (self.streamVarName, castExpr, access, self.ptrVar)) + + def onStringArray(self, vulkanType): + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + if self.direction == "write": + self.cgen.beginBlock() + + self.cgen.stmt("uint32_t c = 0") + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.stmt("c = %s" % (lenAccess)) + if lenAccessGuard is not None: + self.cgen.endIf() + self.genMemcpyAndIncr(self.ptrVar, "(uint32_t*)" ,"&c", "sizeof(uint32_t)", toBe = True, actualSize = 4) + + self.cgen.beginFor("uint32_t i = 0", "i < c", "++i") + self.cgen.stmt("uint32_t l = %s ? strlen(%s[i]): 0" % (access, access)) + self.genMemcpyAndIncr(self.ptrVar, "(uint32_t*)" ,"&l", "sizeof(uint32_t)", toBe = True, actualSize = 4) + self.cgen.beginIf("l") + self.genMemcpyAndIncr(self.ptrVar, "(char*)", "(%s[i])" % access, "l") + self.cgen.endIf() + self.cgen.endFor() + + self.cgen.endBlock() + else: + castExpr = \ + self.makeCastExpr( \ + self.getTypeForStreaming( \ + vulkanType.getForAddressAccess())) + + self.cgen.stmt("%s->loadStringArrayInPlaceWithStreamPtr(%s&%s, %s)" % (self.streamVarName, castExpr, access, self.ptrVar)) + + def onStaticArr(self, vulkanType): + access = self.exprValueAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + finalLenExpr = "%s * %s" % (lenAccess, self.cgen.sizeofExpr(vulkanType)) + self.genStreamCall(vulkanType, access, finalLenExpr) + + # Old version VkEncoder may have some sType values conflict with VkDecoder + # of new versions. For host decoder, it should not carry the incorrect old + # sType values to the |forUnmarshaling| struct. Instead it should overwrite + # the sType value. + def overwriteSType(self, vulkanType): + if self.direction == "read": + sTypeParam = copy(vulkanType) + sTypeParam.paramName = "sType" + sTypeAccess = self.exprAccessor(sTypeParam) + + typeName = vulkanType.parent.typeName + if typeName in STYPE_OVERRIDE: + self.cgen.stmt("%s = %s" % + (sTypeAccess, STYPE_OVERRIDE[typeName])) + + def onStructExtension(self, vulkanType): + self.overwriteSType(vulkanType) + + sTypeParam = copy(vulkanType) + sTypeParam.paramName = "sType" + + access = self.exprAccessor(vulkanType) + sizeVar = "%s_size" % vulkanType.paramName + + if self.direction == "read": + castedAccessExpr = "(%s)(%s)" % ("void*", access) + else: + castedAccessExpr = access + + sTypeAccess = self.exprAccessor(sTypeParam) + self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" % self.rootTypeVarName) + self.cgen.stmt("%s = %s" % (self.rootTypeVarName, sTypeAccess)) + self.cgen.endIf() + + if self.direction == "read" and self.dynAlloc: + self.cgen.stmt("uint32_t %s" % sizeVar) + + self.genMemcpyAndIncr(self.ptrVar, "(uint32_t*)", "&" + sizeVar, "sizeof(uint32_t)", toBe = True, actualSize = 4) + + self.cgen.stmt("%s = nullptr" % access) + self.cgen.beginIf(sizeVar) + self.cgen.stmt( \ + "%s->alloc((void**)&%s, sizeof(VkStructureType))" % + (self.streamVarName, access)) + + self.genStreamCall(vulkanType, access, "sizeof(VkStructureType)") + self.cgen.stmt("VkStructureType extType = *(VkStructureType*)(%s)" % access) + self.cgen.stmt( \ + "%s->alloc((void**)&%s, %s(%s->getFeatureBits(), %s, %s))" % + (self.streamVarName, access, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, self.streamVarName, self.rootTypeVarName, access)) + self.cgen.stmt("*(VkStructureType*)%s = extType" % access) + + self.cgen.funcCall(None, self.marshalPrefix + "extension_struct", + [self.streamVarName, self.rootTypeVarName, castedAccessExpr, self.ptrVar]) + self.cgen.endIf() + else: + + self.cgen.funcCall(None, self.marshalPrefix + "extension_struct", + [self.streamVarName, self.rootTypeVarName, castedAccessExpr, self.ptrVar]) + + def onPointer(self, vulkanType): + access = self.exprAccessor(vulkanType) + + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + self.beginFilterGuard(vulkanType) + self.doAllocSpace(vulkanType) + + if vulkanType.filterVar != None: + print("onPointer Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + + if vulkanType.isHandleType() and self.mapHandles: + self.genHandleMappingCall( + vulkanType, access, lenAccess, lenAccessGuard) + else: + if self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + if lenAccess is not None: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + self.cgen.beginFor("uint32_t i = 0", "i < (uint32_t)%s" % lenAccess, "++i") + self.genPrimitiveStreamCall(vulkanType.getForValueAccess(), "%s[i]" % access) + self.cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + else: + self.genPrimitiveStreamCall(vulkanType.getForValueAccess(), "(*%s)" % access) + else: + if lenAccess is not None: + finalLenExpr = "%s * %s" % ( + lenAccess, self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + else: + finalLenExpr = "%s" % ( + self.cgen.sizeofExpr(vulkanType.getForValueAccess())) + self.genStreamCall(vulkanType, access, finalLenExpr) + + if self.direction == "read": + self.endFilterGuard(vulkanType, "%s = 0" % access) + else: + self.endFilterGuard(vulkanType) + + def onValue(self, vulkanType): + self.beginFilterGuard(vulkanType) + + if vulkanType.isHandleType() and self.mapHandles: + access = self.exprAccessor(vulkanType) + if vulkanType.filterVar != None: + print("onValue Needs filter: %s filterVar %s" % (access, vulkanType.filterVar)) + self.genHandleMappingCall( + vulkanType.getForAddressAccess(), access, "1", None) + elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName): + access = self.exprPrimitiveValueAccessor(vulkanType) + self.genPrimitiveStreamCall(vulkanType, access) + else: + access = self.exprAccessor(vulkanType) + self.genStreamCall(vulkanType, access, self.cgen.sizeofExpr(vulkanType)) + + self.endFilterGuard(vulkanType) + + def streamLetParameter(self, structInfo, letParamInfo): + filterFeature = "%s->getFeatureBits() & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.streamVarName + self.cgen.stmt("%s %s = 1" % (letParamInfo.typeName, letParamInfo.paramName)) + + self.cgen.beginIf(filterFeature) + + if self.direction == "write": + bodyExpr = self.currentStructInfo.environment[letParamInfo.paramName]["body"] + self.cgen.stmt("%s = %s" % (letParamInfo.paramName, self.genFilterFunc(bodyExpr, self.currentStructInfo.environment))) + + self.genPrimitiveStreamCall(letParamInfo, letParamInfo.paramName) + + self.cgen.endIf() + +class VulkanReservedMarshaling(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo, variant="host"): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.cgenHeader = CodeGen() + self.cgenImpl = CodeGen() + + self.variant = variant + + self.currentFeature = None + self.apiOpcodes = {} + self.dynAlloc = self.variant != "guest" + self.ptrVarName = "ptr" + self.ptrVarType = makeVulkanTypeSimple(False, "uint8_t", 2, self.ptrVarName) + self.ptrVarTypeUnmarshal = makeVulkanTypeSimple(False, "uint8_t", 2, self.ptrVarName) + + if self.variant == "guest": + self.marshalingParams = PARAMETERS_MARSHALING_GUEST + else: + self.marshalingParams = PARAMETERS_MARSHALING + + self.writeCodegen = \ + VulkanReservedMarshalingCodegen( + None, + self.variant, + VULKAN_STREAM_VAR_NAME, + ROOT_TYPE_VAR_NAME, + MARSHAL_INPUT_VAR_NAME, + self.ptrVarName, + API_PREFIX_RESERVEDMARSHAL, + "get_host_u64_" if "guest" == self.variant else "", + direction = "write") + + self.readCodegen = \ + VulkanReservedMarshalingCodegen( + None, + self.variant, + VULKAN_STREAM_VAR_NAME, + ROOT_TYPE_VAR_NAME, + UNMARSHAL_INPUT_VAR_NAME, + self.ptrVarName, + API_PREFIX_RESERVEDUNMARSHAL, + "unbox_" if "host" == self.variant else "", + direction = "read", + dynAlloc=self.dynAlloc) + + self.knownDefs = {} + + self.extensionMarshalPrototype = \ + VulkanAPI(API_PREFIX_RESERVEDMARSHAL + "extension_struct", + STREAM_RET_TYPE, + self.marshalingParams + + [STRUCT_EXTENSION_PARAM, self.ptrVarType]) + + self.extensionUnmarshalPrototype = \ + VulkanAPI(API_PREFIX_RESERVEDUNMARSHAL + "extension_struct", + STREAM_RET_TYPE, + self.marshalingParams + + [STRUCT_EXTENSION_PARAM_FOR_WRITE, self.ptrVarTypeUnmarshal]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.cgenImpl.makeFuncDecl(self.extensionMarshalPrototype)) + self.module.appendImpl(self.cgenImpl.makeFuncDecl(self.extensionUnmarshalPrototype)) + + def onBeginFeature(self, featureName, featureType): + VulkanWrapperGenerator.onBeginFeature(self, featureName, featureType) + self.currentFeature = featureName + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + if self.variant != "host": + self.module.appendHeader( + self.cgenHeader.makeFuncAlias(API_PREFIX_RESERVEDMARSHAL + name, + API_PREFIX_RESERVEDMARSHAL + alias)) + if self.variant != "guest": + self.module.appendHeader( + self.cgenHeader.makeFuncAlias(API_PREFIX_RESERVEDUNMARSHAL + name, + API_PREFIX_RESERVEDUNMARSHAL + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + marshalParams = self.marshalingParams + \ + [makeVulkanTypeSimple(True, name, 1, MARSHAL_INPUT_VAR_NAME), + self.ptrVarType] + + freeParams = [] + letParams = [] + + for (envname, bindingInfo) in list(sorted(structInfo.environment.items(), key = lambda kv: kv[0])): + if None == bindingInfo["binding"]: + freeParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + else: + if not bindingInfo["structmember"]: + letParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname)) + + marshalPrototype = \ + VulkanAPI(API_PREFIX_RESERVEDMARSHAL + name, + STREAM_RET_TYPE, + marshalParams + freeParams) + + marshalPrototypeNoFilter = \ + VulkanAPI(API_PREFIX_RESERVEDMARSHAL + name, + STREAM_RET_TYPE, + marshalParams) + + def structMarshalingCustom(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + marshalingCode = \ + CUSTOM_MARSHAL_TYPES[name]["common"] + \ + CUSTOM_MARSHAL_TYPES[name]["reservedmarshaling"].format( + streamVarName=self.writeCodegen.streamVarName, + rootTypeVarName=self.writeCodegen.rootTypeVarName, + inputVarName=self.writeCodegen.inputVarName, + newInputVarName=self.writeCodegen.inputVarName + "_new") + for line in marshalingCode.split('\n'): + cgen.line(line) + + def structMarshalingDef(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + self.writeCodegen.cgen.stmt("(void)%s" % VULKAN_STREAM_VAR_NAME) + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.writeCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.writeCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.writeCodegen) + + def structMarshalingDefNoFilter(cgen): + self.writeCodegen.cgen = cgen + self.writeCodegen.currentStructInfo = structInfo + self.writeCodegen.doFiltering = False + self.writeCodegen.cgen.stmt("(void)%s" % VULKAN_STREAM_VAR_NAME) + self.writeCodegen.cgen.stmt("(void)%s" % ROOT_TYPE_VAR_NAME) + + if category == "struct": + # marshal 'let' parameters first + for letp in letParams: + self.writeCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.writeCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.writeCodegen) + self.writeCodegen.doFiltering = True + + if self.variant != "host": + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(marshalPrototype)) + + if name in CUSTOM_MARSHAL_TYPES: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototype, structMarshalingCustom)) + else: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototype, structMarshalingDef)) + + if freeParams != []: + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(marshalPrototypeNoFilter)) + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + marshalPrototypeNoFilter, structMarshalingDefNoFilter)) + + unmarshalPrototype = \ + VulkanAPI(API_PREFIX_RESERVEDUNMARSHAL + name, + STREAM_RET_TYPE, + self.marshalingParams + [makeVulkanTypeSimple(False, name, 1, UNMARSHAL_INPUT_VAR_NAME), self.ptrVarTypeUnmarshal] + freeParams) + + unmarshalPrototypeNoFilter = \ + VulkanAPI(API_PREFIX_RESERVEDUNMARSHAL + name, + STREAM_RET_TYPE, + self.marshalingParams + [makeVulkanTypeSimple(False, name, 1, UNMARSHAL_INPUT_VAR_NAME), self.ptrVarTypeUnmarshal]) + + def structUnmarshalingCustom(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + unmarshalingCode = \ + CUSTOM_MARSHAL_TYPES[name]["common"] + \ + CUSTOM_MARSHAL_TYPES[name]["reservedunmarshaling"].format( + streamVarName=self.readCodegen.streamVarName, + rootTypeVarName=self.readCodegen.rootTypeVarName, + inputVarName=self.readCodegen.inputVarName, + newInputVarName=self.readCodegen.inputVarName + "_new") + for line in unmarshalingCode.split('\n'): + cgen.line(line) + + def structUnmarshalingDef(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + if category == "struct": + # unmarshal 'let' parameters first + for letp in letParams: + self.readCodegen.streamLetParameter(self.typeInfo, letp) + + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.readCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.readCodegen) + + def structUnmarshalingDefNoFilter(cgen): + self.readCodegen.cgen = cgen + self.readCodegen.currentStructInfo = structInfo + self.readCodegen.doFiltering = False + if category == "struct": + # unmarshal 'let' parameters first + for letp in letParams: + iterateVulkanType(self.typeInfo, letp, self.readCodegen) + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, self.readCodegen) + if category == "union": + iterateVulkanType(self.typeInfo, structInfo.members[0], self.readCodegen) + self.readCodegen.doFiltering = True + + if self.variant != "guest": + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(unmarshalPrototype)) + + if name in CUSTOM_MARSHAL_TYPES: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototype, structUnmarshalingCustom)) + else: + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototype, structUnmarshalingDef)) + + if freeParams != []: + self.module.appendHeader( + self.cgenHeader.makeFuncDecl(unmarshalPrototypeNoFilter)) + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + unmarshalPrototypeNoFilter, structUnmarshalingDefNoFilter)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def doExtensionStructMarshalingCodegen(self, cgen, retType, extParam, forEach, funcproto, direction): + accessVar = "structAccess" + sizeVar = "currExtSize" + cgen.stmt("VkInstanceCreateInfo* %s = (VkInstanceCreateInfo*)(%s)" % (accessVar, extParam.paramName)) + cgen.stmt("uint32_t %s = %s(%s->getFeatureBits(), %s, %s)" % (sizeVar, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME, VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, extParam.paramName)) + + cgen.beginIf("!%s && %s" % (sizeVar, extParam.paramName)) + + cgen.line("// unknown struct extension; skip and call on its pNext field"); + cgen.funcCall(None, funcproto.name, ["vkStream", ROOT_TYPE_VAR_NAME, "(void*)%s->pNext" % accessVar, self.ptrVarName]) + cgen.stmt("return") + + cgen.endIf() + cgen.beginElse() + + cgen.line("// known or null extension struct") + + streamNamespace = "gfxstream::guest" if self.variant == "guest" else "android::base" + + if direction == "write": + cgen.stmt("memcpy(*%s, &%s, sizeof(uint32_t));" % (self.ptrVarName, sizeVar)) + cgen.stmt("%s::Stream::toBe32((uint8_t*)*%s); *%s += sizeof(uint32_t)" % (streamNamespace, self.ptrVarName, self.ptrVarName)) + elif not self.dynAlloc: + cgen.stmt("memcpy(&%s, *%s, sizeof(uint32_t));" % (sizeVar, self.ptrVarName)) + cgen.stmt("%s::Stream::fromBe32((uint8_t*)&%s); *%s += sizeof(uint32_t)" % (streamNamespace, sizeVar, self.ptrVarName)) + + cgen.beginIf("!%s" % (sizeVar)) + cgen.line("// exit if this was a null extension struct (size == 0 in this branch)") + cgen.stmt("return") + cgen.endIf() + + cgen.endIf() + + # Now we can do stream stuff + if direction == "write": + cgen.stmt("memcpy(*%s, %s, sizeof(VkStructureType)); *%s += sizeof(VkStructureType)" % (self.ptrVarName, extParam.paramName, self.ptrVarName)) + elif not self.dynAlloc: + cgen.stmt("uint64_t pNext_placeholder") + placeholderAccess = "(&pNext_placeholder)" + cgen.stmt("memcpy(%s, *%s, sizeof(VkStructureType)); *%s += sizeof(VkStructureType)" % (placeholderAccess, self.ptrVarName, self.ptrVarName)) + cgen.stmt("(void)pNext_placeholder") + + def fatalDefault(cgen): + cgen.line("// fatal; the switch is only taken if the extension struct is known"); + cgen.stmt("abort()") + pass + + self.emitForEachStructExtension( + cgen, + retType, + extParam, + forEach, + defaultEmit=fatalDefault, + rootTypeVar=ROOT_TYPE_PARAM) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def forEachExtensionMarshal(ext, castedAccess, cgen): + cgen.funcCall(None, API_PREFIX_RESERVEDMARSHAL + ext.name, + [VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, castedAccess, self.ptrVarName]) + + def forEachExtensionUnmarshal(ext, castedAccess, cgen): + cgen.funcCall(None, API_PREFIX_RESERVEDUNMARSHAL + ext.name, + [VULKAN_STREAM_VAR_NAME, ROOT_TYPE_VAR_NAME, castedAccess, self.ptrVarName]) + + if self.variant != "host": + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + self.extensionMarshalPrototype, + lambda cgen: self.doExtensionStructMarshalingCodegen( + cgen, + STREAM_RET_TYPE, + STRUCT_EXTENSION_PARAM, + forEachExtensionMarshal, + self.extensionMarshalPrototype, + "write"))) + + if self.variant != "guest": + self.module.appendImpl( + self.cgenImpl.makeFuncImpl( + self.extensionUnmarshalPrototype, + lambda cgen: self.doExtensionStructMarshalingCodegen( + cgen, + STREAM_RET_TYPE, + STRUCT_EXTENSION_PARAM_FOR_WRITE, + forEachExtensionUnmarshal, + self.extensionUnmarshalPrototype, + "read"))) diff --git a/src/gfxstream/codegen/scripts/cereal/subdecode.py b/src/gfxstream/codegen/scripts/cereal/subdecode.py new file mode 100644 index 00000000000..3de2213a189 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/subdecode.py @@ -0,0 +1,405 @@ +from .common.codegen import CodeGen, VulkanWrapperGenerator +from .common.vulkantypes import VulkanAPI, iterateVulkanType, VulkanType + +from .reservedmarshaling import VulkanReservedMarshalingCodegen +from .transform import TransformCodegen + +from .wrapperdefs import API_PREFIX_RESERVEDUNMARSHAL +from .wrapperdefs import MAX_PACKET_LENGTH +from .wrapperdefs import ROOT_TYPE_DEFAULT_VALUE + + +decoder_decl_preamble = """ +""" + +decoder_impl_preamble = """ +""" + +global_state_prefix = "this->on_" + +READ_STREAM = "readStream" +WRITE_STREAM = "vkStream" + +# Driver workarounds for APIs that don't work well multithreaded +driver_workarounds_global_lock_apis = [ + "vkCreatePipelineLayout", + "vkDestroyPipelineLayout", +] + +MAX_STACK_ITEMS = "16" + + +def emit_param_decl_for_reading(param, cgen): + if param.staticArrExpr: + cgen.stmt( + cgen.makeRichCTypeDecl(param.getForNonConstAccess())) + else: + cgen.stmt( + cgen.makeRichCTypeDecl(param)) + + if param.pointerIndirectionLevels > 0: + lenAccess = cgen.generalLengthAccess(param) + if not lenAccess: + lenAccess = "1" + arrSize = "1" if "1" == lenAccess else "MAX_STACK_ITEMS" + + typeHere = "uint8_t*" if "void" == param.typeName else param.typeName + cgen.stmt("%s%s stack_%s[%s]" % ( + typeHere, "*" * (param.pointerIndirectionLevels - 1), param.paramName, arrSize)) + + +def emit_unmarshal(typeInfo, param, cgen, output=False, destroy=False, noUnbox=False): + if destroy: + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "", + direction="read", + dynAlloc=True)) + lenAccess = cgen.generalLengthAccess(param) + lenAccessGuard = cgen.generalLengthAccessGuard(param) + if None == lenAccess or "1" == lenAccess: + cgen.stmt("boxed_%s_preserve = %s" % + (param.paramName, param.paramName)) + cgen.stmt("%s = unbox_%s(%s)" % + (param.paramName, param.typeName, param.paramName)) + else: + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i") + cgen.stmt("boxed_%s_preserve[i] = %s[i]" % + (param.paramName, param.paramName)) + cgen.stmt("((%s*)(%s))[i] = unbox_%s(%s[i])" % (param.typeName, + param.paramName, param.typeName, param.paramName)) + cgen.endFor() + if lenAccessGuard is not None: + self.cgen.endIf() + else: + if noUnbox: + cgen.line("// No unbox for %s" % (param.paramName)) + + lenAccess = cgen.generalLengthAccess(param) + if not lenAccess: + lenAccess = "1" + arrSize = "1" if "1" == lenAccess else "MAX_STACK_ITEMS" + + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "" if (output or noUnbox) else "unbox_", + direction="read", + dynAlloc=True, + stackVar="stack_%s" % param.paramName, + stackArrSize=arrSize)) + + +def emit_dispatch_unmarshal(typeInfo, param, cgen, globalWrapped): + if globalWrapped: + cgen.stmt( + "// Begin global wrapped dispatchable handle unboxing for %s" % param.paramName) + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "", + direction="read", + dynAlloc=True)) + else: + cgen.stmt( + "// Begin non wrapped dispatchable handle unboxing for %s" % param.paramName) + # cgen.stmt("%s->unsetHandleMapping()" % READ_STREAM) + iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen( + cgen, + "host", + READ_STREAM, + ROOT_TYPE_DEFAULT_VALUE, + param.paramName, + "readStreamPtrPtr", + API_PREFIX_RESERVEDUNMARSHAL, + "", + direction="read", + dynAlloc=True)) + cgen.stmt("auto unboxed_%s = unbox_%s(%s)" % + (param.paramName, param.typeName, param.paramName)) + cgen.stmt("auto vk = dispatch_%s(%s)" % + (param.typeName, param.paramName)) + cgen.stmt("// End manual dispatchable handle unboxing for %s" % + param.paramName) + + +def emit_transform(typeInfo, param, cgen, variant="tohost"): + res = \ + iterateVulkanType(typeInfo, param, TransformCodegen( + cgen, param.paramName, "globalstate", "transform_%s_" % variant, variant)) + if not res: + cgen.stmt("(void)%s" % param.paramName) + +# Everything here elides the initial arg + + +class DecodingParameters(object): + def __init__(self, api: VulkanAPI): + self.params: list[VulkanType] = [] + self.toRead: list[VulkanType] = [] + self.toWrite: list[VulkanType] = [] + + for i, param in enumerate(api.parameters[1:]): + if i == 0 and param.isDispatchableHandleType(): + param.dispatchHandle = True + + if param.isNonDispatchableHandleType() and param.isCreatedBy(api): + param.nonDispatchableHandleCreate = True + + if param.isNonDispatchableHandleType() and param.isDestroyedBy(api): + param.nonDispatchableHandleDestroy = True + + if param.isDispatchableHandleType() and param.isCreatedBy(api): + param.dispatchableHandleCreate = True + + if param.isDispatchableHandleType() and param.isDestroyedBy(api): + param.dispatchableHandleDestroy = True + + self.toRead.append(param) + + if param.possiblyOutput(): + self.toWrite.append(param) + + self.params.append(param) + + +def emit_call_log(api, cgen): + decodingParams = DecodingParameters(api) + paramsToRead = decodingParams.toRead + + # cgen.beginIf("m_logCalls") + paramLogFormat = "%p" + paramLogArgs = ["(void*)boxed_dispatchHandle"] + + for p in paramsToRead: + paramLogFormat += "0x%llx " + for p in paramsToRead: + paramLogArgs.append("(unsigned long long)%s" % (p.paramName)) + # cgen.stmt("fprintf(stderr, \"substream %%p: call %s %s\\n\", readStream, %s)" % (api.name, paramLogFormat, ", ".join(paramLogArgs))) + # cgen.endIf() + + +def emit_decode_parameters(typeInfo, api, cgen, globalWrapped=False): + + decodingParams = DecodingParameters(api) + + paramsToRead = decodingParams.toRead + + for p in paramsToRead: + emit_param_decl_for_reading(p, cgen) + + i = 0 + for p in paramsToRead: + lenAccess = cgen.generalLengthAccess(p) + + if p.dispatchHandle: + emit_dispatch_unmarshal(typeInfo, p, cgen, globalWrapped) + else: + destroy = p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy + noUnbox = False + + if p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy: + destroy = True + cgen.stmt( + "// Begin manual non dispatchable handle destroy unboxing for %s" % p.paramName) + if None == lenAccess or "1" == lenAccess: + cgen.stmt("%s boxed_%s_preserve" % + (p.typeName, p.paramName)) + else: + cgen.stmt("%s* boxed_%s_preserve; %s->alloc((void**)&boxed_%s_preserve, %s * sizeof(%s))" % + (p.typeName, p.paramName, READ_STREAM, p.paramName, lenAccess, p.typeName)) + + if p.possiblyOutput(): + cgen.stmt( + "// Begin manual dispatchable handle unboxing for %s" % p.paramName) + cgen.stmt("%s->unsetHandleMapping()" % READ_STREAM) + + emit_unmarshal(typeInfo, p, cgen, output=p.possiblyOutput( + ), destroy=destroy, noUnbox=noUnbox) + i += 1 + + for p in paramsToRead: + emit_transform(typeInfo, p, cgen, variant="tohost") + + emit_call_log(api, cgen) + + +def emit_dispatch_call(api, cgen): + + decodingParams = DecodingParameters(api) + + customParams = ["(VkCommandBuffer)dispatchHandle"] + + for (i, p) in enumerate(api.parameters[1:]): + customParam = p.paramName + if decodingParams.params[i].dispatchHandle: + customParam = "unboxed_%s" % p.paramName + customParams.append(customParam) + + if api.name in driver_workarounds_global_lock_apis: + cgen.stmt("lock()") + + cgen.vkApiCall(api, customPrefix="vk->", customParameters=customParams, + checkForDeviceLost=True, globalStatePrefix=global_state_prefix, + checkForOutOfMemory=True) + + if api.name in driver_workarounds_global_lock_apis: + cgen.stmt("unlock()") + + +def emit_global_state_wrapped_call(api, cgen, context=False): + customParams = ["pool", "(VkCommandBuffer)(boxed_dispatchHandle)"] + \ + list(map(lambda p: p.paramName, api.parameters[1:])) + if context: + customParams += ["context"]; + cgen.vkApiCall(api, customPrefix=global_state_prefix, + customParameters=customParams, checkForDeviceLost=True, + checkForOutOfMemory=True, globalStatePrefix=global_state_prefix) + + +def emit_default_decoding(typeInfo, api, cgen): + emit_decode_parameters(typeInfo, api, cgen) + emit_dispatch_call(api, cgen) + + +def emit_global_state_wrapped_decoding(typeInfo, api, cgen): + emit_decode_parameters(typeInfo, api, cgen, globalWrapped=True) + emit_global_state_wrapped_call(api, cgen) + +def emit_global_state_wrapped_decoding_with_context(typeInfo, api, cgen): + emit_decode_parameters(typeInfo, api, cgen, globalWrapped=True) + emit_global_state_wrapped_call(api, cgen, context=True) + +custom_decodes = { + "vkCmdCopyBufferToImage": emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage": emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer": emit_global_state_wrapped_decoding, + "vkCmdCopyBufferToImage2": emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage2": emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer2": emit_global_state_wrapped_decoding, + "vkCmdCopyBufferToImage2KHR": emit_global_state_wrapped_decoding_with_context, + "vkCmdCopyImage2KHR": emit_global_state_wrapped_decoding, + "vkCmdCopyImageToBuffer2KHR": emit_global_state_wrapped_decoding, + "vkCmdExecuteCommands": emit_global_state_wrapped_decoding, + "vkBeginCommandBuffer": emit_global_state_wrapped_decoding_with_context, + "vkEndCommandBuffer": emit_global_state_wrapped_decoding_with_context, + "vkResetCommandBuffer": emit_global_state_wrapped_decoding, + "vkCmdPipelineBarrier": emit_global_state_wrapped_decoding, + "vkCmdBindPipeline": emit_global_state_wrapped_decoding, + "vkCmdBindDescriptorSets": emit_global_state_wrapped_decoding, + "vkCmdCopyQueryPoolResults": emit_global_state_wrapped_decoding, + "vkBeginCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding_with_context, + "vkEndCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding_with_context, + "vkResetCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding, + "vkCommandBufferHostSyncGOOGLE": emit_global_state_wrapped_decoding, +} + + +class VulkanSubDecoder(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + self.typeInfo = typeInfo + self.cgen = CodeGen() + + def onBegin(self,): + self.module.appendImpl( + "#define MAX_STACK_ITEMS %s\n" % MAX_STACK_ITEMS) + + self.module.appendImpl( + "#define MAX_PACKET_LENGTH %s\n" % MAX_PACKET_LENGTH) + + self.module.appendImpl( + "size_t subDecode(VulkanMemReadingStream* readStream, VulkanDispatch* vk, void* boxed_dispatchHandle, void* dispatchHandle, VkDeviceSize dataSize, const void* pData, const VkDecoderContext& context)\n") + + self.cgen.beginBlock() # function body + + self.cgen.stmt("auto& metricsLogger = *context.metricsLogger") + self.cgen.stmt("uint32_t count = 0") + self.cgen.stmt("unsigned char *buf = (unsigned char *)pData") + self.cgen.stmt("android::base::BumpPool* pool = readStream->pool()") + self.cgen.stmt("unsigned char *ptr = (unsigned char *)pData") + self.cgen.stmt( + "const unsigned char* const end = (const unsigned char*)buf + dataSize") + self.cgen.stmt( + "VkDecoderGlobalState* globalstate = VkDecoderGlobalState::get()") + + self.cgen.line("while (end - ptr >= 8)") + self.cgen.beginBlock() # while loop + + self.cgen.stmt("uint32_t opcode = *(uint32_t *)ptr") + self.cgen.stmt("uint32_t packetLen = *(uint32_t *)(ptr + 4)") + self.cgen.line(""" + // packetLen should be at least 8 (op code and packet length) and should not be excessively large + if (packetLen < 8 || packetLen > MAX_PACKET_LENGTH) { + WARN("Bad packet length %d detected, subdecode may fail", packetLen); + metricsLogger.logMetricEvent(MetricEventBadPacketLength{ .len = packetLen }); + } + """) + self.cgen.stmt("if (end - ptr < packetLen) return ptr - (unsigned char*)buf") + + + self.cgen.stmt("%s->setBuf((uint8_t*)(ptr + 8))" % READ_STREAM) + self.cgen.stmt( + "uint8_t* readStreamPtr = %s->getBuf(); uint8_t** readStreamPtrPtr = &readStreamPtr" % READ_STREAM) + self.cgen.line("switch (opcode)") + self.cgen.beginBlock() # switch stmt + + self.module.appendImpl(self.cgen.swapCode()) + + def onGenCmd(self, cmdinfo, name, alias): + typeInfo = self.typeInfo + cgen = self.cgen + api = typeInfo.apis[name] + + if "commandBuffer" != api.parameters[0].paramName: + return + + cgen.line("case OP_%s:" % name) + cgen.beginBlock() + cgen.stmt("android::base::beginTrace(\"%s subdecode\")" % name) + + if api.name in custom_decodes.keys(): + custom_decodes[api.name](typeInfo, api, cgen) + else: + emit_default_decoding(typeInfo, api, cgen) + + cgen.stmt("android::base::endTrace()") + cgen.stmt("break") + cgen.endBlock() + self.module.appendImpl(self.cgen.swapCode()) + + def onEnd(self,): + self.cgen.line("default:") + self.cgen.beginBlock() + self.cgen.stmt( + "GFXSTREAM_ABORT(::emugl::FatalError(::emugl::ABORT_REASON_OTHER)) << \"Unrecognized opcode \" << opcode") + self.cgen.endBlock() + + self.cgen.endBlock() # switch stmt + + self.cgen.stmt("++count; if (count % 1000 == 0) { pool->freeAll(); }") + self.cgen.stmt("ptr += packetLen") + self.cgen.endBlock() # while loop + + self.cgen.stmt("pool->freeAll()") + self.cgen.stmt("return ptr - (unsigned char*)buf;") + self.cgen.endBlock() # function body + self.module.appendImpl(self.cgen.swapCode()) diff --git a/src/gfxstream/codegen/scripts/cereal/testing.py b/src/gfxstream/codegen/scripts/cereal/testing.py new file mode 100644 index 00000000000..898532fc946 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/testing.py @@ -0,0 +1,399 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import copy + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import EQUALITY_VAR_NAMES +from .wrapperdefs import EQUALITY_ON_FAIL_VAR +from .wrapperdefs import EQUALITY_ON_FAIL_VAR_TYPE +from .wrapperdefs import EQUALITY_RET_TYPE +from .wrapperdefs import API_PREFIX_EQUALITY +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM2 + +class VulkanEqualityCodegen(VulkanTypeIterator): + + def __init__(self, cgen, inputVars, onFailCompareVar, prefix): + self.cgen = cgen + self.inputVars = inputVars + self.onFailCompareVar = onFailCompareVar + self.prefix = prefix + + def makeAccess(varName, asPtr = True): + return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr) + + def makeLengthAccess(varName): + return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName) + + def makeLengthAccessGuard(varName): + return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName) + + self.exprAccessorLhs = makeAccess(self.inputVars[0]) + self.exprAccessorRhs = makeAccess(self.inputVars[1]) + + self.exprAccessorValueLhs = makeAccess(self.inputVars[0], asPtr = False) + self.exprAccessorValueRhs = makeAccess(self.inputVars[1], asPtr = False) + + self.lenAccessorLhs = makeLengthAccess(self.inputVars[0]) + self.lenAccessorRhs = makeLengthAccess(self.inputVars[1]) + + self.lenAccessGuardLhs = makeLengthAccessGuard(self.inputVars[0]) + self.lenAccessGuardRhs = makeLengthAccessGuard(self.inputVars[1]) + + self.checked = False + + def getTypeForCompare(self, vulkanType): + res = copy(vulkanType) + + if not vulkanType.accessibleAsPointer(): + res = res.getForAddressAccess() + + if vulkanType.staticArrExpr: + res = res.getForAddressAccess() + + return res + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def makeEqualExpr(self, lhs, rhs): + return "(%s) == (%s)" % (lhs, rhs) + + def makeEqualBufExpr(self, lhs, rhs, size): + return "(memcmp(%s, %s, %s) == 0)" % (lhs, rhs, size) + + def makeEqualStringExpr(self, lhs, rhs): + return "(strcmp(%s, %s) == 0)" % (lhs, rhs) + + def makeBothNotNullExpr(self, lhs, rhs): + return "(%s) && (%s)" % (lhs, rhs) + + def makeBothNullExpr(self, lhs, rhs): + return "!(%s) && !(%s)" % (lhs, rhs) + + def compareWithConsequence(self, compareExpr, vulkanType, errMsg=""): + self.cgen.stmt("if (!(%s)) { %s(\"%s (Error: %s)\"); }" % + (compareExpr, self.onFailCompareVar, + self.exprAccessorValueLhs(vulkanType), errMsg)) + + def onCheck(self, vulkanType): + + self.checked = True + + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + bothNull = self.makeBothNullExpr(accessLhs, accessRhs) + bothNotNull = self.makeBothNotNullExpr(accessLhs, accessRhs) + nullMatchExpr = "(%s) || (%s)" % (bothNull, bothNotNull) + + self.compareWithConsequence( \ + nullMatchExpr, + vulkanType, + "Mismatch in optional field") + + skipStreamInternal = vulkanType.typeName == "void" + + if skipStreamInternal: + return + + self.cgen.beginIf("%s && %s" % (accessLhs, accessRhs)) + + def endCheck(self, vulkanType): + + skipStreamInternal = vulkanType.typeName == "void" + if skipStreamInternal: + return + + if self.checked: + self.cgen.endIf() + self.checked = False + + def onCompoundType(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + lenAccessRhs = self.lenAccessorRhs(vulkanType) + + lenAccessGuardLhs = self.lenAccessGuardLhs(vulkanType) + lenAccessGuardRhs = self.lenAccessGuardRhs(vulkanType) + + needNullCheck = vulkanType.pointerIndirectionLevels > 0 + + if needNullCheck: + bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs) + self.cgen.beginIf(bothNotNullExpr) + + if lenAccessLhs is not None: + equalLenExpr = self.makeEqualExpr(lenAccessLhs, lenAccessRhs) + + self.compareWithConsequence( \ + equalLenExpr, + vulkanType, "Lengths not equal") + + loopVar = "i" + accessLhs = "%s + %s" % (accessLhs, loopVar) + accessRhs = "%s + %s" % (accessRhs, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs) + forIncr = "++%s" % loopVar + + if needNullCheck: + self.cgen.beginIf(equalLenExpr) + + if lenAccessGuardLhs is not None: + self.cgen.beginIf(lenAccessGuardLhs) + + self.cgen.beginFor(forInit, forCond, forIncr) + + self.cgen.funcCall(None, self.prefix + vulkanType.typeName, + [accessLhs, accessRhs, self.onFailCompareVar]) + + if lenAccessLhs is not None: + self.cgen.endFor() + if lenAccessGuardLhs is not None: + self.cgen.endIf() + if needNullCheck: + self.cgen.endIf() + + if needNullCheck: + self.cgen.endIf() + + def onString(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + bothNullExpr = self.makeBothNullExpr(accessLhs, accessRhs) + bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs) + nullMatchExpr = "(%s) || (%s)" % (bothNullExpr, bothNotNullExpr) + + self.compareWithConsequence( \ + nullMatchExpr, + vulkanType, + "Mismatch in string pointer nullness") + + self.cgen.beginIf(bothNotNullExpr) + + self.compareWithConsequence( + self.makeEqualStringExpr(accessLhs, accessRhs), + vulkanType, "Unequal strings") + + self.cgen.endIf() + + def onStringArray(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + lenAccessRhs = self.lenAccessorRhs(vulkanType) + + lenAccessGuardLhs = self.lenAccessGuardLhs(vulkanType) + lenAccessGuardRhs = self.lenAccessGuardRhs(vulkanType) + + bothNullExpr = self.makeBothNullExpr(accessLhs, accessRhs) + bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs) + nullMatchExpr = "(%s) || (%s)" % (bothNullExpr, bothNotNullExpr) + + self.compareWithConsequence( \ + nullMatchExpr, + vulkanType, + "Mismatch in string array pointer nullness") + + equalLenExpr = self.makeEqualExpr(lenAccessLhs, lenAccessRhs) + + self.compareWithConsequence( \ + equalLenExpr, + vulkanType, "Lengths not equal in string array") + + self.compareWithConsequence( \ + equalLenExpr, + vulkanType, "Lengths not equal in string array") + + self.cgen.beginIf("%s && %s" % (equalLenExpr, bothNotNullExpr)) + + loopVar = "i" + accessLhs = "*(%s + %s)" % (accessLhs, loopVar) + accessRhs = "*(%s + %s)" % (accessRhs, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs) + forIncr = "++%s" % loopVar + + if lenAccessGuardLhs is not None: + self.cgen.beginIf(lenAccessGuardLhs) + + self.cgen.beginFor(forInit, forCond, forIncr) + + self.compareWithConsequence( + self.makeEqualStringExpr(accessLhs, accessRhs), + vulkanType, "Unequal string in string array") + + self.cgen.endFor() + + if lenAccessGuardLhs is not None: + self.cgen.endIf() + + self.cgen.endIf() + + def onStaticArr(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + + finalLenExpr = "%s * %s" % (lenAccessLhs, + self.cgen.sizeofExpr(vulkanType)) + + self.compareWithConsequence( + self.makeEqualBufExpr(accessLhs, accessRhs, finalLenExpr), + vulkanType, "Unequal static array") + + def onStructExtension(self, vulkanType): + lhs = self.exprAccessorLhs(vulkanType) + rhs = self.exprAccessorRhs(vulkanType) + + self.cgen.beginIf(lhs) + self.cgen.funcCall(None, self.prefix + "extension_struct", + [lhs, rhs, self.onFailCompareVar]) + self.cgen.endIf() + + def onPointer(self, vulkanType): + accessLhs = self.exprAccessorLhs(vulkanType) + accessRhs = self.exprAccessorRhs(vulkanType) + + skipStreamInternal = vulkanType.typeName == "void" + if skipStreamInternal: + return + + lenAccessLhs = self.lenAccessorLhs(vulkanType) + lenAccessRhs = self.lenAccessorRhs(vulkanType) + + if lenAccessLhs is not None: + self.compareWithConsequence( \ + self.makeEqualExpr(lenAccessLhs, lenAccessRhs), + vulkanType, "Lengths not equal") + + finalLenExpr = "%s * %s" % (lenAccessLhs, + self.cgen.sizeofExpr( + vulkanType.getForValueAccess())) + else: + finalLenExpr = self.cgen.sizeofExpr(vulkanType.getForValueAccess()) + + self.compareWithConsequence( + self.makeEqualBufExpr(accessLhs, accessRhs, finalLenExpr), + vulkanType, "Unequal dyn array") + + def onValue(self, vulkanType): + accessLhs = self.exprAccessorValueLhs(vulkanType) + accessRhs = self.exprAccessorValueRhs(vulkanType) + self.compareWithConsequence( + self.makeEqualExpr(accessLhs, accessRhs), vulkanType, + "Value not equal") + + +class VulkanTesting(VulkanWrapperGenerator): + + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.equalityCodegen = \ + VulkanEqualityCodegen( + None, + EQUALITY_VAR_NAMES, + EQUALITY_ON_FAIL_VAR, + API_PREFIX_EQUALITY) + + self.knownDefs = {} + + self.extensionTestingPrototype = \ + VulkanAPI(API_PREFIX_EQUALITY + "extension_struct", + EQUALITY_RET_TYPE, + [STRUCT_EXTENSION_PARAM, + STRUCT_EXTENSION_PARAM2, + EQUALITY_ON_FAIL_VAR_TYPE]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + self.module.appendImpl(self.codegen.makeFuncDecl( + self.extensionTestingPrototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownDefs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + self.module.appendHeader( + self.codegen.makeFuncAlias(API_PREFIX_EQUALITY + name, + API_PREFIX_EQUALITY + alias)) + + if category in ["struct", "union"] and not alias: + + structInfo = self.typeInfo.structs[name] + + typeFromName = \ + lambda varname: makeVulkanTypeSimple(True, name, 1, varname) + + compareParams = \ + list(map(typeFromName, EQUALITY_VAR_NAMES)) + \ + [EQUALITY_ON_FAIL_VAR_TYPE] + + comparePrototype = \ + VulkanAPI(API_PREFIX_EQUALITY + name, + EQUALITY_RET_TYPE, + compareParams) + + def structCompareDef(cgen): + self.equalityCodegen.cgen = cgen + for member in structInfo.members: + iterateVulkanType(self.typeInfo, member, + self.equalityCodegen) + + self.module.appendHeader( + self.codegen.makeFuncDecl(comparePrototype)) + self.module.appendImpl( + self.codegen.makeFuncImpl(comparePrototype, structCompareDef)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + def forEachExtensionCompare(ext, castedAccess, cgen): + cgen.funcCall(None, API_PREFIX_EQUALITY + ext.name, + [castedAccess, + cgen.makeReinterpretCast( + STRUCT_EXTENSION_PARAM2.paramName, ext.name), + EQUALITY_ON_FAIL_VAR]) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + self.extensionTestingPrototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + EQUALITY_RET_TYPE, + STRUCT_EXTENSION_PARAM, + forEachExtensionCompare))) diff --git a/src/gfxstream/codegen/scripts/cereal/transform.py b/src/gfxstream/codegen/scripts/cereal/transform.py new file mode 100644 index 00000000000..79aff0e3a8c --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/transform.py @@ -0,0 +1,348 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanCompoundType, VulkanAPI, makeVulkanTypeSimple, vulkanTypeNeedsTransform, vulkanTypeGetNeededTransformTypes, VulkanTypeIterator, iterateVulkanType, vulkanTypeforEachSubType, TRIVIAL_TRANSFORMED_TYPES, NON_TRIVIAL_TRANSFORMED_TYPES, TRANSFORMED_TYPES + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE + +def deviceMemoryTransform(resourceTrackerVarName, structOrApiInfo, getExpr, getLen, cgen, variant="tohost"): + paramIndices = \ + structOrApiInfo.deviceMemoryInfoParameterIndices + + for _, info in paramIndices.items(): + orderedKeys = [ + "handle", + "offset", + "size", + "typeIndex", + "typeBits",] + + casts = { + "handle" : "VkDeviceMemory*", + "offset" : "VkDeviceSize*", + "size" : "VkDeviceSize*", + "typeIndex" : "uint32_t*", + "typeBits" : "uint32_t*", + } + + accesses = { + "handle" : "nullptr", + "offset" : "nullptr", + "size" : "nullptr", + "typeIndex" : "nullptr", + "typeBits" : "nullptr", + } + + lenAccesses = { + "handle" : "0", + "offset" : "0", + "size" : "0", + "typeIndex" : "0", + "typeBits" : "0", + } + + def doParam(i, vulkanType): + access = getExpr(vulkanType) + lenAccess = getLen(vulkanType) + + for k in orderedKeys: + if i == info.__dict__[k]: + accesses[k] = access + if lenAccess is not None: + lenAccesses[k] = lenAccess + else: + lenAccesses[k] = "1" + + vulkanTypeforEachSubType(structOrApiInfo, doParam) + + callParams = ", ".join( \ + ["(%s)%s, %s" % (casts[k], accesses[k], lenAccesses[k]) \ + for k in orderedKeys]) + + if variant == "tohost": + cgen.stmt("%s->deviceMemoryTransform_tohost(%s)" % \ + (resourceTrackerVarName, callParams)) + else: + cgen.stmt("%s->deviceMemoryTransform_fromhost(%s)" % \ + (resourceTrackerVarName, callParams)) + +def directTransform(resourceTrackerVarName, vulkanType, getExpr, getLen, cgen, variant="tohost"): + access = getExpr(vulkanType) + lenAccess = getLen(vulkanType) + + if lenAccess: + finalLenAccess = lenAccess + else: + finalLenAccess = "1" + + cgen.stmt("%s->transformImpl_%s_%s(%s, %s)" % (resourceTrackerVarName, + vulkanType.typeName, variant, access, finalLenAccess)) + +def genTransformsForVulkanType(resourceTrackerVarName, structOrApiInfo, getExpr, getLen, cgen, variant="tohost"): + for transform in vulkanTypeGetNeededTransformTypes(structOrApiInfo): + if transform == "devicememory": + deviceMemoryTransform( \ + resourceTrackerVarName, + structOrApiInfo, + getExpr, getLen, cgen, variant=variant) + +class TransformCodegen(VulkanTypeIterator): + def __init__(self, cgen, inputVar, resourceTrackerVarName, prefix, variant): + self.cgen = cgen + self.inputVar = inputVar + self.prefix = prefix + self.resourceTrackerVarName = resourceTrackerVarName + + def makeAccess(varName, asPtr = True): + return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr) + + def makeLengthAccess(varName): + return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName) + + def makeLengthAccessGuard(varName): + return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName) + + self.exprAccessor = makeAccess(self.inputVar) + self.exprAccessorValue = makeAccess(self.inputVar, asPtr = False) + self.lenAccessor = makeLengthAccess(self.inputVar) + self.lenAccessorGuard = makeLengthAccessGuard(self.inputVar) + + self.checked = False + + self.variant = variant + + def makeCastExpr(self, vulkanType): + return "(%s)" % ( + self.cgen.makeCTypeDecl(vulkanType, useParamName=False)) + + def asNonConstCast(self, access, vulkanType): + if vulkanType.staticArrExpr: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + elif vulkanType.accessibleAsPointer(): + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access) + else: + casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access) + return casted + + def onCheck(self, vulkanType): + pass + + def endCheck(self, vulkanType): + pass + + def onCompoundType(self, vulkanType): + + access = self.exprAccessor(vulkanType) + lenAccess = self.lenAccessor(vulkanType) + lenAccessGuard = self.lenAccessorGuard(vulkanType) + + isPtr = vulkanType.pointerIndirectionLevels > 0 + + if lenAccessGuard is not None: + self.cgen.beginIf(lenAccessGuard) + + if isPtr: + self.cgen.beginIf(access) + + if lenAccess is not None: + + loopVar = "i" + access = "%s + %s" % (access, loopVar) + forInit = "uint32_t %s = 0" % loopVar + forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess) + forIncr = "++%s" % loopVar + + self.cgen.beginFor(forInit, forCond, forIncr) + + accessCasted = self.asNonConstCast(access, vulkanType) + + if vulkanType.isTransformed: + directTransform(self.resourceTrackerVarName, vulkanType, self.exprAccessor, self.lenAccessor, self.cgen, variant=self.variant) + + self.cgen.funcCall(None, self.prefix + vulkanType.typeName, + [self.resourceTrackerVarName, accessCasted]) + + if lenAccess is not None: + self.cgen.endFor() + + if isPtr: + self.cgen.endIf() + + if lenAccessGuard is not None: + self.cgen.endIf() + + def onString(self, vulkanType): + pass + + def onStringArray(self, vulkanType): + pass + + def onStaticArr(self, vulkanType): + pass + + def onStructExtension(self, vulkanType): + access = self.exprAccessor(vulkanType) + + castedAccessExpr = "(%s)(%s)" % ("void*", access) + self.cgen.beginIf(access) + self.cgen.funcCall(None, self.prefix + "extension_struct", + [self.resourceTrackerVarName, castedAccessExpr]) + self.cgen.endIf() + + def onPointer(self, vulkanType): + pass + + def onValue(self, vulkanType): + pass + + +class VulkanTransform(VulkanWrapperGenerator): + def __init__(self, module, typeInfo, resourceTrackerTypeName="ResourceTracker", resourceTrackerVarName="resourceTracker"): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.transformPrefix = "transform_" + + self.tohostpart = "tohost" + self.fromhostpart = "fromhost" + self.variants = [self.tohostpart, self.fromhostpart] + + self.toTransformVar = "toTransform" + self.resourceTrackerTypeName = resourceTrackerTypeName + self.resourceTrackerVarName = resourceTrackerVarName + self.transformParam = \ + makeVulkanTypeSimple(False, self.resourceTrackerTypeName, 1, + self.resourceTrackerVarName) + self.voidType = makeVulkanTypeSimple(False, "void", 0) + + self.extensionTransformPrototypes = [] + + for variant in self.variants: + self.extensionTransformPrototypes.append( \ + VulkanAPI(self.transformPrefix + variant + "_extension_struct", + self.voidType, + [self.transformParam, STRUCT_EXTENSION_PARAM_FOR_WRITE])) + + self.knownStructs = {} + self.needsTransform = set([]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + # Set up a convenience macro fro the transformed structs + # and forward-declare the resource tracker class + self.codegen.stmt("class %s" % self.resourceTrackerTypeName) + self.codegen.line("#define LIST_TRIVIAL_TRANSFORMED_TYPES(f) \\") + for name in TRIVIAL_TRANSFORMED_TYPES: + self.codegen.line("f(%s) \\" % name) + self.codegen.line("") + + self.codegen.line("#define LIST_NON_TRIVIAL_TRANSFORMED_TYPES(f) \\") + for name in NON_TRIVIAL_TRANSFORMED_TYPES: + self.codegen.line("f(%s) \\" % name) + self.codegen.line("") + + self.codegen.line("#define LIST_TRANSFORMED_TYPES(f) \\") + self.codegen.line("LIST_TRIVIAL_TRANSFORMED_TYPES(f) \\") + self.codegen.line("LIST_NON_TRIVIAL_TRANSFORMED_TYPES(f) \\") + self.codegen.line("") + + self.module.appendHeader(self.codegen.swapCode()) + + for prototype in self.extensionTransformPrototypes: + self.module.appendImpl(self.codegen.makeFuncDecl( + prototype)) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownStructs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + for variant in self.variants: + self.module.appendHeader( + self.codegen.makeFuncAlias(self.transformPrefix + variant + "_" + name, + self.transformPrefix + variant + "_" + alias)) + + if category in ["struct", "union"] and not alias: + structInfo = self.typeInfo.structs[name] + self.knownStructs[name] = structInfo + + for variant in self.variants: + api = VulkanAPI( \ + self.transformPrefix + variant + "_" + name, + self.voidType, + [self.transformParam] + \ + [makeVulkanTypeSimple( \ + False, name, 1, self.toTransformVar)]) + + transformer = TransformCodegen( + None, + self.toTransformVar, + self.resourceTrackerVarName, + self.transformPrefix + variant + "_", + variant) + + def funcDefGenerator(cgen): + transformer.cgen = cgen + for p in api.parameters: + cgen.stmt("(void)%s" % p.paramName) + + genTransformsForVulkanType( + self.resourceTrackerVarName, + structInfo, + transformer.exprAccessor, + transformer.lenAccessor, + cgen, + variant=variant) + + for member in structInfo.members: + iterateVulkanType( + self.typeInfo, member, + transformer) + + self.module.appendHeader( + self.codegen.makeFuncDecl(api)) + self.module.appendImpl( + self.codegen.makeFuncImpl(api, funcDefGenerator)) + + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) + + for (variant, prototype) in zip(self.variants, self.extensionTransformPrototypes): + def forEachExtensionTransform(ext, castedAccess, cgen): + if ext.isTransformed: + directTransform(self.resourceTrackerVarName, ext, lambda _ : castedAccess, lambda _ : "1", cgen, variant); + cgen.funcCall(None, self.transformPrefix + variant + "_" + ext.name, + [self.resourceTrackerVarName, castedAccess]) + + self.module.appendImpl( + self.codegen.makeFuncImpl( + prototype, + lambda cgen: self.emitForEachStructExtension( + cgen, + self.voidType, + STRUCT_EXTENSION_PARAM_FOR_WRITE, + forEachExtensionTransform))) diff --git a/src/gfxstream/codegen/scripts/cereal/unbox.py b/src/gfxstream/codegen/scripts/cereal/unbox.py new file mode 100644 index 00000000000..f18fa2717e7 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/unbox.py @@ -0,0 +1,81 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import CodeGen +from .common.vulkantypes import \ + VulkanCompoundType, VulkanAPI, makeVulkanTypeSimple, vulkanTypeNeedsTransform, vulkanTypeGetNeededTransformTypes, VulkanTypeIterator, iterateVulkanType, vulkanTypeforEachSubType, TRANSFORMED_TYPES + +from .wrapperdefs import VulkanWrapperGenerator +from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE + +# This is different from others; it operations solely in terms of deepcopy and handlemap +class VulkanUnbox(VulkanWrapperGenerator): + def __init__(self, module, typeInfo): + VulkanWrapperGenerator.__init__(self, module, typeInfo) + + self.codegen = CodeGen() + + self.unboxPrefix = "unbox" + self.toUnboxVar = "toUnbox" + self.poolParam = \ + makeVulkanTypeSimple(False, "BumpPool", 1, "pool") + + self.knownStructs = {} + self.needsTransform = set([]) + + def onBegin(self,): + VulkanWrapperGenerator.onBegin(self) + + def onGenType(self, typeXml, name, alias): + VulkanWrapperGenerator.onGenType(self, typeXml, name, alias) + + if name in self.knownStructs: + return + + category = self.typeInfo.categoryOf(name) + + if category in ["struct", "union"] and alias: + self.module.appendHeader( + self.codegen.makeFuncAlias(self.unboxPrefix + "_" + name, + self.unboxPrefix + "_" + alias)) + + if category in ["struct", "union"] and not alias: + structInfo = self.typeInfo.structs[name] + self.knownStructs[name] = structInfo + + api = VulkanAPI( \ + self.unboxPrefix + "_" + name, + makeVulkanTypeSimple(False, name, 1), + [self.poolParam] + \ + [makeVulkanTypeSimple( \ + True, name, 1, self.toUnboxVar)]) + + def funcDefGenerator(cgen): + cgen.stmt("BoxedHandleUnwrapMapping unboxMapping") + cgen.stmt("%s* res = (%s*)pool->alloc(sizeof(const %s))" % (name, name, name)) + cgen.stmt("deepcopy_%s(pool, %s, %s)" % (name, self.toUnboxVar, "res")) + cgen.stmt("handlemap_%s(%s, %s)" % (name, "&unboxMapping", "res")) + cgen.stmt("return res") + + self.module.appendHeader( + self.codegen.makeFuncDecl(api)) + self.module.appendImpl( + self.codegen.makeFuncImpl(api, funcDefGenerator)) + + def onGenCmd(self, cmdinfo, name, alias): + VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias) + + def onEnd(self,): + VulkanWrapperGenerator.onEnd(self) diff --git a/src/gfxstream/codegen/scripts/cereal/vkextensionstructuretype.py b/src/gfxstream/codegen/scripts/cereal/vkextensionstructuretype.py new file mode 100644 index 00000000000..8db988aea79 --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/vkextensionstructuretype.py @@ -0,0 +1,46 @@ +# Copyright (c) 2022 The Android Open Source Project +# Copyright (c) 2022 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .wrapperdefs import VulkanWrapperGenerator + + +class VulkanExtensionStructureType(VulkanWrapperGenerator): + def __init__(self, extensionName: str, module, typeInfo): + super().__init__(module, typeInfo) + self._extensionName = extensionName + + def onGenGroup(self, groupinfo, groupName, alias=None): + super().onGenGroup(groupinfo, groupName, alias) + elem = groupinfo.elem + if (not elem.get('type') == 'enum'): + return + if (not elem.get('name') == 'VkStructureType'): + return + extensionEnumFactoryMacro = f'{self._extensionName.upper()}_ENUM' + for enum in elem.findall(f"enum[@extname='{self._extensionName}']"): + name = enum.get('name') + offset = enum.get('offset') + self.module.appendHeader( + f"#define {name} {extensionEnumFactoryMacro}(VkStructureType, {offset})\n") + + +class VulkanGfxstreamStructureType(VulkanExtensionStructureType): + def __init__(self, module, typeInfo): + super().__init__('VK_GOOGLE_gfxstream', module, typeInfo) + + +class VulkanAndroidNativeBufferStructureType(VulkanExtensionStructureType): + def __init__(self, module, typeInfo): + super().__init__('VK_ANDROID_native_buffer', module, typeInfo) diff --git a/src/gfxstream/codegen/scripts/cereal/wrapperdefs.py b/src/gfxstream/codegen/scripts/cereal/wrapperdefs.py new file mode 100644 index 00000000000..9b96987559d --- /dev/null +++ b/src/gfxstream/codegen/scripts/cereal/wrapperdefs.py @@ -0,0 +1,108 @@ +# Copyright (c) 2018 The Android Open Source Project +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .common.codegen import VulkanWrapperGenerator +from .common.vulkantypes import makeVulkanTypeSimple + +# Contains definitions for various Vulkan API wrappers. This information is +# shared to make it easier for one kind of wrapper to know how to call +# another one. + +API_PREFIX_MARSHAL = "marshal_" +API_PREFIX_UNMARSHAL = "unmarshal_" +API_PREFIX_RESERVEDMARSHAL = "reservedmarshal_" +API_PREFIX_RESERVEDUNMARSHAL = "reservedunmarshal_" + +MARSHAL_INPUT_VAR_NAME = "forMarshaling" +UNMARSHAL_INPUT_VAR_NAME = "forUnmarshaling" + +API_PREFIX_VALIDATE = "validate_" +API_PREFIX_FRONTEND = "goldfish_frontend_" + +VULKAN_STREAM_TYPE = "VulkanStream" +VULKAN_STREAM_TYPE_GUEST = "VulkanStreamGuest" +VULKAN_STREAM_VAR_NAME = "vkStream" + +VALIDATE_RESULT_TYPE = "VkResult" +VALIDATE_VAR_NAME = "validateResult" +VALIDATE_GOOD_RESULT = "VK_SUCCESS" + +ROOT_TYPE_VAR_NAME = "rootType" +ROOT_TYPE_DEFAULT_VALUE = "VK_STRUCTURE_TYPE_MAX_ENUM" +ROOT_TYPE_TYPE = "VkStructureType" +ROOT_TYPE_PARAM = makeVulkanTypeSimple( + False, ROOT_TYPE_TYPE, 0, ROOT_TYPE_VAR_NAME) + +PARAMETERS_MARSHALING = [ + makeVulkanTypeSimple(False, VULKAN_STREAM_TYPE, 1, VULKAN_STREAM_VAR_NAME), + ROOT_TYPE_PARAM, +] +PARAMETERS_MARSHALING_GUEST = [ + makeVulkanTypeSimple(False, VULKAN_STREAM_TYPE_GUEST, + 1, VULKAN_STREAM_VAR_NAME), + ROOT_TYPE_PARAM, +] +PARAMETERS_VALIDATE = [ + makeVulkanTypeSimple(False, VALIDATE_RESULT_TYPE, 1, VALIDATE_VAR_NAME) +] +PARAMETERS_COUNTING = [ + makeVulkanTypeSimple(False, "size_t", 1, VULKAN_STREAM_VAR_NAME) +] + +STRUCT_EXTENSION_PARAM = \ + makeVulkanTypeSimple(True, "void", 1, "structExtension") + +STRUCT_EXTENSION_PARAM2 = \ + makeVulkanTypeSimple(True, "void", 1, "structExtension2") + +STRUCT_EXTENSION_PARAM_FOR_WRITE = \ + makeVulkanTypeSimple(False, "void", 1, "structExtension_out") + +STRUCT_TYPE_API_NAME = "goldfish_vk_struct_type" +EXTENSION_SIZE_API_NAME = "goldfish_vk_extension_struct_size" +EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME = "goldfish_vk_extension_struct_size_with_stream_features" + +VOID_TYPE = makeVulkanTypeSimple(False, "void", 0) +STREAM_RET_TYPE = makeVulkanTypeSimple(False, "void", 0) + +API_PREFIX_EQUALITY = "checkEqual_" +EQUALITY_VAR_NAMES = ["a", "b"] +EQUALITY_ON_FAIL_VAR = "onFail" +EQUALITY_ON_FAIL_VAR_TYPE = makeVulkanTypeSimple(False, "OnFailCompareFunc", 0, + EQUALITY_ON_FAIL_VAR) +EQUALITY_RET_TYPE = makeVulkanTypeSimple(False, "void", 0) + +RELAXED_APIS = [ + "vkWaitForFences", + "vkWaitSemaphores", + "vkWaitSemaphoresKHR", + "vkQueueWaitIdle", + "vkDeviceWaitIdle", + "vkQueueFlushCommandsGOOGLE", +] + +STYPE_OVERRIDE = { + "VkPhysicalDeviceFragmentDensityMapFeaturesEXT": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT", + "VkPhysicalDeviceFragmentDensityMapPropertiesEXT": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT", + "VkRenderPassFragmentDensityMapCreateInfoEXT": "VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT", + "VkImportColorBufferGOOGLE": "VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE", + "VkImportBufferGOOGLE": "VK_STRUCTURE_TYPE_IMPORT_BUFFER_GOOGLE", + "VkCreateBlobGOOGLE": "VK_STRUCTURE_TYPE_CREATE_BLOB_GOOGLE", +} + +MAX_PACKET_LENGTH = "(400 * 1024 * 1024) // 400MB" + + + diff --git a/src/gfxstream/codegen/scripts/cerealgenerator.py b/src/gfxstream/codegen/scripts/cerealgenerator.py new file mode 100644 index 00000000000..13fe908bc6a --- /dev/null +++ b/src/gfxstream/codegen/scripts/cerealgenerator.py @@ -0,0 +1,892 @@ +#!/usr/bin/python3 -i +# +# Copyright (c) 2013-2018 The Khronos Group Inc. +# Copyright (c) 2013-2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os, re, sys +from generator import * +from pathlib import Path, PurePosixPath + +import cereal +from cereal.wrapperdefs import VULKAN_STREAM_TYPE +from cereal.wrapperdefs import VULKAN_STREAM_TYPE_GUEST + +# CerealGenerator - generates set of driver sources +# while being agnostic to the stream implementation +from reg import GroupInfo, TypeInfo, EnumInfo + +SUPPORTED_FEATURES = [ + "VK_VERSION_1_0", + "VK_VERSION_1_1", + "VK_VERSION_1_2", + "VK_VERSION_1_3", + # Instance extensions + "VK_KHR_get_physical_device_properties2", + "VK_KHR_sampler_ycbcr_conversion", + "VK_KHR_external_semaphore_capabilities", + "VK_KHR_external_memory_capabilities", + "VK_KHR_external_fence_capabilities", + # Device extensions + "VK_KHR_storage_buffer_storage_class", + "VK_KHR_vulkan_memory_model", + "VK_KHR_buffer_device_address", + "VK_KHR_maintenance1", + "VK_KHR_maintenance2", + "VK_KHR_maintenance3", + "VK_KHR_bind_memory2", + "VK_KHR_dedicated_allocation", + "VK_KHR_get_memory_requirements2", + "VK_KHR_sampler_ycbcr_conversion", + "VK_KHR_shader_float16_int8", + "VK_AMD_gpu_shader_half_float", + "VK_NV_shader_subgroup_partitioned", + "VK_KHR_shader_subgroup_extended_types", + "VK_EXT_provoking_vertex", + "VK_EXT_line_rasterization", + "VK_EXT_transform_feedback", + "VK_EXT_primitive_topology_list_restart", + "VK_EXT_index_type_uint8", + "VK_EXT_load_store_op_none", + "VK_EXT_swapchain_colorspace", + "VK_EXT_custom_border_color", + "VK_EXT_shader_stencil_export", + "VK_KHR_image_format_list", + "VK_KHR_incremental_present", + "VK_KHR_pipeline_executable_properties", + "VK_EXT_queue_family_foreign", + "VK_KHR_external_semaphore", + "VK_KHR_external_semaphore_fd", + "VK_KHR_external_memory", + "VK_KHR_external_fence", + "VK_KHR_external_fence_fd", + "VK_EXT_device_memory_report", + "VK_KHR_create_renderpass2", + "VK_KHR_imageless_framebuffer", + "VK_KHR_descriptor_update_template", + # see aosp/2736079 + b/268351352 + "VK_EXT_swapchain_maintenance1", + "VK_EXT_image_compression_control", + "VK_EXT_image_compression_control_swapchain", + # VK1.3 extensions: see b/298704840 + "VK_KHR_copy_commands2", + "VK_KHR_dynamic_rendering", + "VK_KHR_format_feature_flags2", + "VK_KHR_maintenance4", + "VK_KHR_shader_integer_dot_product", + "VK_KHR_shader_non_semantic_info", + "VK_KHR_shader_terminate_invocation", + "VK_KHR_synchronization2", + "VK_KHR_zero_initialize_workgroup_memory", + "VK_EXT_4444_formats", + "VK_EXT_extended_dynamic_state", + "VK_EXT_extended_dynamic_state2", + "VK_EXT_image_robustness", + "VK_EXT_inline_uniform_block", + "VK_EXT_pipeline_creation_cache_control", + "VK_EXT_pipeline_creation_feedback", + "VK_EXT_private_data", + "VK_EXT_shader_demote_to_helper_invocation", + "VK_EXT_subgroup_size_control", + "VK_EXT_texel_buffer_alignment", + "VK_EXT_texture_compression_astc_hdr", + "VK_EXT_tooling_info", + "VK_EXT_ycbcr_2plane_444_formats", + # Host dispatch + "VK_EXT_debug_utils", + "VK_KHR_surface", + "VK_KHR_swapchain", + "VK_KHR_xcb_surface", + "VK_KHR_win32_surface", + "VK_EXT_metal_surface", + "VK_MVK_moltenvk", + "VK_KHR_external_semaphore_win32", + "VK_KHR_external_memory_win32", + "VK_KHR_external_memory_fd", + # Android + "VK_ANDROID_native_buffer", + "VK_ANDROID_external_memory_android_hardware_buffer", + "VK_KHR_android_surface", + # Custom + "VK_GOOGLE_gfxstream", + # Used in tests without proper support checks + "VK_EXT_graphics_pipeline_library", +] + +# By default, the all wrappers are run all on all features. In certain cases, +# we wish run only a subset of wrappers. For example, `VK_GOOGLE_gfxstream` +# shouldn't generate a function table entry since it's an internal interface. +SUPPORTED_WRAPPERS = { + "VK_EXT_debug_utils": [cereal.VulkanDispatch], + "VK_KHR_surface": [cereal.VulkanDispatch], + "VK_KHR_xcb_surface": [cereal.VulkanDispatch], + "VK_KHR_win32_surface": [cereal.VulkanDispatch], + "VK_EXT_metal_surface": [cereal.VulkanDispatch], + # VK_MVK_moltenvk doesn't generate a generate dispatch entry for some reason, but should. The + # lack of this extension doesn't cause any build failtures though. + "VK_MVK_moltenvk": [cereal.VulkanDispatch], + "VK_KHR_external_semaphore_win32" : [cereal.VulkanDispatch], + "VK_KHR_external_memory_win32" : [cereal.VulkanDispatch], + "VK_KHR_external_memory_fd": [cereal.VulkanDispatch], + "VK_ANDROID_external_memory_android_hardware_buffer": [cereal.VulkanFuncTable], + "VK_KHR_android_surface": [cereal.VulkanFuncTable], +} + +copyrightHeader = """// Copyright (C) 2018 The Android Open Source Project +// Copyright (C) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +""" + +# We put the long generated commands in a separate paragraph, so that the formatter won't mess up +# with other texts. +autogeneratedHeaderTemplate = """ +// Autogenerated module %s +// +// %s +// +// Please do not modify directly; +// re-run gfxstream-protocols/scripts/generate-vulkan-sources.sh, +// or directly from Python by defining: +// VULKAN_REGISTRY_XML_DIR : Directory containing vk.xml +// VULKAN_REGISTRY_SCRIPTS_DIR : Directory containing genvk.py +// CEREAL_OUTPUT_DIR: Where to put the generated sources. +// +// python3 $VULKAN_REGISTRY_SCRIPTS_DIR/genvk.py -registry $VULKAN_REGISTRY_XML_DIR/vk.xml cereal -o $CEREAL_OUTPUT_DIR +// +""" + +autogeneratedMkTemplate = """ +# Autogenerated makefile +# %s +# Please do not modify directly; +# re-run gfxstream-protocols/scripts/generate-vulkan-sources.sh, +# or directly from Python by defining: +# VULKAN_REGISTRY_XML_DIR : Directory containing vk.xml +# VULKAN_REGISTRY_SCRIPTS_DIR : Directory containing genvk.py +# CEREAL_OUTPUT_DIR: Where to put the generated sources. +# python3 $VULKAN_REGISTRY_SCRIPTS_DIR/genvk.py -registry $VULKAN_REGISTRY_XML_DIR/vk.xml cereal -o $CEREAL_OUTPUT_DIR +""" + +namespaceBegin =""" +namespace gfxstream { +namespace vk {\n +""" + +namespaceEnd = """ +} // namespace vk +} // namespace gfxstream +""" + +def banner_command(argv): + """Return sanitized command-line description. + |argv| must be a list of command-line parameters, e.g. sys.argv. + Return a string corresponding to the command, with platform-specific + paths removed.""" + + def makePosixRelative(someArg): + if os.path.exists(someArg): + return str(PurePosixPath(Path(os.path.relpath(someArg)))) + return someArg + + return ' '.join(map(makePosixRelative, argv)) + +def envGetOrDefault(key, default=None): + if key in os.environ: + return os.environ[key] + print("envGetOrDefault: notfound: %s" % key) + return default + +# ---- methods overriding base class ---- +# beginFile(genOpts) +# endFile() +# beginFeature(interface, emit) +# endFeature() +# genType(typeinfo,name) +# genStruct(typeinfo,name) +# genGroup(groupinfo,name) +# genEnum(enuminfo, name) +# genCmd(cmdinfo) +class CerealGenerator(OutputGenerator): + + """Generate serialization code""" + def __init__(self, errFile = sys.stderr, + warnFile = sys.stderr, + diagFile = sys.stdout): + OutputGenerator.__init__(self, errFile, warnFile, diagFile) + + self.typeInfo = cereal.VulkanTypeInfo(self) + + self.modules = {} + self.protos = {} + self.moduleList = [] + self.protoList = [] + + self.wrappers = [] + + self.codegen = cereal.CodeGen() + self.featureSupported = False + self.supportedWrappers = None + + self.guestBaseLibDirPrefix = \ + envGetOrDefault("VK_CEREAL_GUEST_BASELIB_PREFIX", "aemu/base") + self.baseLibDirPrefix = \ + envGetOrDefault("VK_CEREAL_BASELIB_PREFIX", "aemu/base") + self.baseLibLinkName = \ + envGetOrDefault("VK_CEREAL_BASELIB_LINKNAME", "android-emu-base") + self.vulkanHeaderTargetName = envGetOrDefault("VK_CEREAL_VK_HEADER_TARGET", "") + self.utilsHeader = envGetOrDefault("VK_CEREAL_UTILS_LINKNAME", "") + self.utilsHeaderDirPrefix = envGetOrDefault("VK_CEREAL_UTILS_PREFIX", "utils") + + # THe host always needs all possible guest struct definitions, while the guest only needs + # platform sepcific headers. + self.hostCommonExtraVulkanHeaders = '#include "vk_android_native_buffer.h"' + self.host_cmake_generator = lambda cppFiles: f"""{autogeneratedMkTemplate % banner_command(sys.argv)} +add_library(OpenglRender_vulkan_cereal {cppFiles}) +target_compile_definitions(OpenglRender_vulkan_cereal PRIVATE -DVK_GOOGLE_gfxstream) +if (WIN32) + target_compile_definitions(OpenglRender_vulkan_cereal PRIVATE -DVK_USE_PLATFORM_WIN32_KHR) +endif() +target_link_libraries( + OpenglRender_vulkan_cereal + PUBLIC + {self.baseLibLinkName} + {self.vulkanHeaderTargetName} + PRIVATE + {self.utilsHeader}) + +target_include_directories(OpenglRender_vulkan_cereal + PUBLIC + . + PRIVATE + .. + ../.. + ../../../include) +""" + + encoderInclude = f""" +#include "{self.guestBaseLibDirPrefix}/AndroidHealthMonitor.h" +#include "goldfish_vk_private_defs.h" +#include + +namespace gfxstream {{ +namespace guest {{ +class IOStream; +}} // namespace guest +}} // namespace gfxstream +""" + encoderImplInclude = f""" +#include "EncoderDebug.h" +#include "IOStream.h" +#include "Resources.h" +#include "ResourceTracker.h" +#include "Validation.h" +#include "%s.h" + +#include "{self.guestBaseLibDirPrefix}/AlignedBuf.h" +#include "{self.guestBaseLibDirPrefix}/BumpPool.h" +#include "{self.guestBaseLibDirPrefix}/synchronization/AndroidLock.h" + +#include + +#include "goldfish_vk_marshaling_guest.h" +#include "goldfish_vk_reserved_marshaling_guest.h" +#include "goldfish_vk_deepcopy_guest.h" +#include "goldfish_vk_counting_guest.h" +#include "goldfish_vk_private_defs.h" +#include "goldfish_vk_transform_guest.h" + +#include +#include +#include +#include +#include + +""" % VULKAN_STREAM_TYPE_GUEST + + functableImplInclude = """ +#include "VkEncoder.h" +#include "../OpenglSystemCommon/HostConnection.h" +#include "ResourceTracker.h" + +#include "goldfish_vk_private_defs.h" + +#include +#include + +// Stuff we are not going to use but if included, +// will cause compile errors. These are Android Vulkan +// required extensions, but the approach will be to +// implement them completely on the guest side. +#undef VK_KHR_android_surface +#if defined(LINUX_GUEST_BUILD) +#undef VK_ANDROID_native_buffer +#endif +""" + marshalIncludeGuest = """ +#include "goldfish_vk_marshaling_guest.h" +#include "goldfish_vk_private_defs.h" +#include "%s.h" + +// Stuff we are not going to use but if included, +// will cause compile errors. These are Android Vulkan +// required extensions, but the approach will be to +// implement them completely on the guest side. +#undef VK_KHR_android_surface +#undef VK_ANDROID_external_memory_android_hardware_buffer +""" % VULKAN_STREAM_TYPE_GUEST + + reservedmarshalIncludeGuest = """ +#include "goldfish_vk_marshaling_guest.h" +#include "goldfish_vk_private_defs.h" +#include "%s.h" + +// Stuff we are not going to use but if included, +// will cause compile errors. These are Android Vulkan +// required extensions, but the approach will be to +// implement them completely on the guest side. +#undef VK_KHR_android_surface +#undef VK_ANDROID_external_memory_android_hardware_buffer +""" % VULKAN_STREAM_TYPE_GUEST + + reservedmarshalImplIncludeGuest = """ +#include "Resources.h" +""" + + vulkanStreamIncludeHost = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" + +#include "%s.h" +#include "{self.baseLibDirPrefix}/files/StreamSerializing.h" +""" % VULKAN_STREAM_TYPE + + poolInclude = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" +#include "{self.baseLibDirPrefix}/BumpPool.h" +using android::base::Allocator; +using android::base::BumpPool; +""" + handleMapInclude = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" +#include "VulkanHandleMapping.h" +""" + transformIncludeGuest = """ +#include "goldfish_vk_private_defs.h" +""" + transformInclude = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" +#include "goldfish_vk_extension_structs.h" +""" + transformImplIncludeGuest = """ +#include "ResourceTracker.h" +""" + transformImplInclude = """ +#include "VkDecoderGlobalState.h" +""" + deepcopyInclude = """ +#include "vk_util.h" +""" + poolIncludeGuest = f""" +#include "goldfish_vk_private_defs.h" +#include "{self.guestBaseLibDirPrefix}/BumpPool.h" +using gfxstream::guest::Allocator; +using gfxstream::guest::BumpPool; +// Stuff we are not going to use but if included, +// will cause compile errors. These are Android Vulkan +// required extensions, but the approach will be to +// implement them completely on the guest side. +#undef VK_KHR_android_surface +#undef VK_ANDROID_external_memory_android_hardware_buffer +""" + dispatchHeaderDefs = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" +namespace gfxstream {{ +namespace vk {{ + +struct VulkanDispatch; + +}} // namespace vk +}} // namespace gfxstream +using DlOpenFunc = void* (void); +using DlSymFunc = void* (void*, const char*); +""" + + extensionStructsInclude = f""" +{self.hostCommonExtraVulkanHeaders} +#include "goldfish_vk_private_defs.h" +""" + + extensionStructsIncludeGuest = """ +#include "vk_platform_compat.h" +#include "goldfish_vk_private_defs.h" +// Stuff we are not going to use but if included, +// will cause compile errors. These are Android Vulkan +// required extensions, but the approach will be to +// implement them completely on the guest side. +#undef VK_KHR_android_surface +#undef VK_ANDROID_external_memory_android_hardware_buffer +""" + commonCerealImplIncludes = """ +#include "goldfish_vk_extension_structs.h" +#include "goldfish_vk_private_defs.h" +#include +""" + commonCerealIncludesGuest = """ +#include "vk_platform_compat.h" +""" + commonCerealImplIncludesGuest = """ +#include "goldfish_vk_extension_structs_guest.h" +#include "goldfish_vk_private_defs.h" + +#include +""" + countingIncludes = """ +#include "vk_platform_compat.h" +#include "goldfish_vk_private_defs.h" +""" + + dispatchImplIncludes = """ +#include +#include +#include +""" + + decoderSnapshotHeaderIncludes = f""" +#include +#include "{self.utilsHeaderDirPrefix}/GfxApiLogger.h" +#include "{self.baseLibDirPrefix}/HealthMonitor.h" +#include "common/goldfish_vk_private_defs.h" +""" + decoderSnapshotImplIncludes = f""" +#include "VulkanHandleMapping.h" +#include "VkDecoderGlobalState.h" +#include "VkReconstruction.h" + +#include "{self.baseLibDirPrefix}/synchronization/Lock.h" +""" + + decoderHeaderIncludes = f""" +#include "VkDecoderContext.h" + +#include + +namespace android {{ +namespace base {{ +class BumpPool; +}} // namespace android +}} // namespace base + +""" + + decoderImplIncludes = f""" +#include "common/goldfish_vk_marshaling.h" +#include "common/goldfish_vk_reserved_marshaling.h" +#include "common/goldfish_vk_private_defs.h" +#include "common/goldfish_vk_transform.h" + +#include "{self.baseLibDirPrefix}/BumpPool.h" +#include "{self.baseLibDirPrefix}/system/System.h" +#include "{self.baseLibDirPrefix}/Tracing.h" +#include "{self.baseLibDirPrefix}/Metrics.h" +#include "render-utils/IOStream.h" +#include "host/FrameBuffer.h" +#include "host-common/feature_control.h" +#include "host-common/GfxstreamFatalError.h" +#include "host-common/logging.h" + +#include "VkDecoderGlobalState.h" +#include "VkDecoderSnapshot.h" + +#include "VulkanDispatch.h" +#include "%s.h" + +#include +#include +#include +""" % VULKAN_STREAM_TYPE + + def createVkExtensionStructureTypePreamble(extensionName: str) -> str: + return f""" +#define {extensionName}_ENUM(type,id) \ + ((type)(1000000000 + (1000 * ({extensionName}_NUMBER - 1)) + (id))) +""" + self.guest_encoder_tag = "guest_encoder" + self.host_tag = "host" + + default_guest_abs_encoder_destination = \ + os.path.join( + os.getcwd(), + "..", "..", + "device", "generic", "goldfish-opengl", + "system", "vulkan_enc") + self.guest_abs_encoder_destination = \ + envGetOrDefault("VK_CEREAL_GUEST_ENCODER_DIR", + default_guest_abs_encoder_destination) + + default_host_abs_decoder_destination = \ + os.path.join( + os.getcwd(), + "android", "android-emugl", "host", + "libs", "libOpenglRender", "vulkan") + self.host_abs_decoder_destination = \ + envGetOrDefault("VK_CEREAL_HOST_DECODER_DIR", + default_host_abs_decoder_destination) + self.host_script_destination = envGetOrDefault("VK_CEREAL_HOST_SCRIPTS_DIR") + assert(self.host_script_destination is not None) + + self.addGuestEncoderModule( + "VkEncoder", + extraHeader = encoderInclude, + extraImpl = encoderImplInclude) + + self.addGuestEncoderModule("goldfish_vk_extension_structs_guest", + extraHeader=extensionStructsIncludeGuest) + self.addGuestEncoderModule("goldfish_vk_marshaling_guest", + extraHeader=commonCerealIncludesGuest + marshalIncludeGuest, + extraImpl=commonCerealImplIncludesGuest) + self.addGuestEncoderModule("goldfish_vk_reserved_marshaling_guest", + extraHeader=commonCerealIncludesGuest + reservedmarshalIncludeGuest, + extraImpl=commonCerealImplIncludesGuest + reservedmarshalImplIncludeGuest) + self.addGuestEncoderModule("goldfish_vk_deepcopy_guest", + extraHeader=commonCerealIncludesGuest + poolIncludeGuest, + extraImpl=commonCerealImplIncludesGuest + deepcopyInclude) + self.addGuestEncoderModule("goldfish_vk_counting_guest", + extraHeader=countingIncludes, + extraImpl=commonCerealImplIncludesGuest) + self.addGuestEncoderModule("goldfish_vk_transform_guest", + extraHeader=commonCerealIncludesGuest + transformIncludeGuest, + extraImpl=commonCerealImplIncludesGuest + transformImplIncludeGuest) + self.addGuestEncoderModule( + "vulkan_gfxstream_structure_type", headerOnly=True, suppressFeatureGuards=True, + moduleName="vulkan_gfxstream_structure_type_guest", useNamespace=False, + suppressVulkanHeaders=True, + extraHeader=createVkExtensionStructureTypePreamble('VK_GOOGLE_GFXSTREAM')) + + self.addGuestEncoderModule("func_table", extraImpl=functableImplInclude) + + self.addCppModule("common", "goldfish_vk_extension_structs", + extraHeader=extensionStructsInclude) + self.addCppModule("common", "goldfish_vk_marshaling", + extraHeader=vulkanStreamIncludeHost, + extraImpl=commonCerealImplIncludes) + self.addCppModule("common", "goldfish_vk_reserved_marshaling", + extraHeader=vulkanStreamIncludeHost, + extraImpl=commonCerealImplIncludes) + self.addCppModule("common", "goldfish_vk_deepcopy", + extraHeader=poolInclude, + extraImpl=commonCerealImplIncludes + deepcopyInclude) + self.addCppModule("common", "goldfish_vk_handlemap", + extraHeader=handleMapInclude, + extraImpl=commonCerealImplIncludes) + self.addCppModule("common", "goldfish_vk_dispatch", + extraHeader=dispatchHeaderDefs, + extraImpl=dispatchImplIncludes) + self.addCppModule("common", "goldfish_vk_transform", + extraHeader=transformInclude, + extraImpl=transformImplInclude) + self.addHostModule("VkDecoder", + extraHeader=decoderHeaderIncludes, + extraImpl=decoderImplIncludes, + useNamespace=False) + self.addHostModule("VkDecoderSnapshot", + extraHeader=decoderSnapshotHeaderIncludes, + extraImpl=decoderSnapshotImplIncludes, + useNamespace=False) + self.addHostModule("VkSubDecoder", + extraHeader="", + extraImpl="", + useNamespace=False, + implOnly=True) + + self.addModule(cereal.PyScript(self.host_tag, "vulkan_printer", customAbsDir=Path( + self.host_script_destination) / "print_gfx_logs"), moduleName="ApiLogDecoder") + self.addHostModule( + "vulkan_gfxstream_structure_type", headerOnly=True, suppressFeatureGuards=True, + moduleName="vulkan_gfxstream_structure_type_host", useNamespace=False, + suppressVulkanHeaders=True, + extraHeader=createVkExtensionStructureTypePreamble('VK_GOOGLE_GFXSTREAM')) + self.addHostModule( + "vk_android_native_buffer_structure_type", headerOnly=True, suppressFeatureGuards=True, + useNamespace=False, suppressVulkanHeaders=True, + extraHeader=createVkExtensionStructureTypePreamble('VK_ANDROID_NATIVE_BUFFER')) + + self.addWrapper(cereal.VulkanEncoder, "VkEncoder") + self.addWrapper(cereal.VulkanExtensionStructs, "goldfish_vk_extension_structs_guest") + self.addWrapper(cereal.VulkanMarshaling, "goldfish_vk_marshaling_guest", variant = "guest") + self.addWrapper(cereal.VulkanReservedMarshaling, "goldfish_vk_reserved_marshaling_guest", variant = "guest") + self.addWrapper(cereal.VulkanDeepcopy, "goldfish_vk_deepcopy_guest") + self.addWrapper(cereal.VulkanCounting, "goldfish_vk_counting_guest") + self.addWrapper(cereal.VulkanTransform, "goldfish_vk_transform_guest") + self.addWrapper(cereal.VulkanFuncTable, "func_table") + self.addWrapper(cereal.VulkanExtensionStructs, "goldfish_vk_extension_structs") + self.addWrapper(cereal.VulkanMarshaling, "goldfish_vk_marshaling") + self.addWrapper(cereal.VulkanReservedMarshaling, "goldfish_vk_reserved_marshaling", variant = "host") + self.addWrapper(cereal.VulkanDeepcopy, "goldfish_vk_deepcopy") + self.addWrapper(cereal.VulkanHandleMap, "goldfish_vk_handlemap") + self.addWrapper(cereal.VulkanDispatch, "goldfish_vk_dispatch") + self.addWrapper(cereal.VulkanTransform, "goldfish_vk_transform", resourceTrackerTypeName="VkDecoderGlobalState") + self.addWrapper(cereal.VulkanDecoder, "VkDecoder") + self.addWrapper(cereal.VulkanDecoderSnapshot, "VkDecoderSnapshot") + self.addWrapper(cereal.VulkanSubDecoder, "VkSubDecoder") + self.addWrapper(cereal.ApiLogDecoder, "ApiLogDecoder") + self.addWrapper(cereal.VulkanGfxstreamStructureType, + "vulkan_gfxstream_structure_type_guest") + self.addWrapper(cereal.VulkanGfxstreamStructureType, "vulkan_gfxstream_structure_type_host") + self.addWrapper(cereal.VulkanAndroidNativeBufferStructureType, + "vk_android_native_buffer_structure_type") + + self.guestAndroidMkCppFiles = "" + self.hostCMakeCppFiles = "" + self.hostDecoderCMakeCppFiles = "" + + def addSrcEntry(m): + mkSrcEntry = m.getMakefileSrcEntry() + cmakeSrcEntry = m.getCMakeSrcEntry() + if m.directory == self.guest_encoder_tag: + self.guestAndroidMkCppFiles += mkSrcEntry + elif m.directory == self.host_tag: + self.hostDecoderCMakeCppFiles += cmakeSrcEntry + else: + self.hostCMakeCppFiles += cmakeSrcEntry + + self.forEachModule(addSrcEntry) + + def addGuestEncoderModule( + self, basename, extraHeader="", extraImpl="", useNamespace=True, headerOnly=False, + suppressFeatureGuards=False, moduleName=None, suppressVulkanHeaders=False): + if not os.path.exists(self.guest_abs_encoder_destination): + print("Path [%s] not found (guest encoder path), skipping" % self.guest_abs_encoder_destination) + return + self.addCppModule(self.guest_encoder_tag, basename, extraHeader=extraHeader, + extraImpl=extraImpl, customAbsDir=self.guest_abs_encoder_destination, + useNamespace=useNamespace, headerOnly=headerOnly, + suppressFeatureGuards=suppressFeatureGuards, moduleName=moduleName, + suppressVulkanHeaders=suppressVulkanHeaders) + + def addHostModule( + self, basename, extraHeader="", extraImpl="", useNamespace=True, implOnly=False, + suppress=False, headerOnly=False, suppressFeatureGuards=False, moduleName=None, + suppressVulkanHeaders=False): + if not os.path.exists(self.host_abs_decoder_destination): + print("Path [%s] not found (host encoder path), skipping" % + self.host_abs_decoder_destination) + return + if not suppressVulkanHeaders: + extraHeader = self.hostCommonExtraVulkanHeaders + '\n' + extraHeader + self.addCppModule( + self.host_tag, basename, extraHeader=extraHeader, extraImpl=extraImpl, + customAbsDir=self.host_abs_decoder_destination, useNamespace=useNamespace, + implOnly=implOnly, suppress=suppress, headerOnly=headerOnly, + suppressFeatureGuards=suppressFeatureGuards, moduleName=moduleName, + suppressVulkanHeaders=suppressVulkanHeaders) + + def addModule(self, module, moduleName=None): + if moduleName is None: + moduleName = module.basename + self.moduleList.append(moduleName) + self.modules[moduleName] = module + + def addCppModule( + self, directory, basename, extraHeader="", extraImpl="", customAbsDir=None, + useNamespace=True, implOnly=False, suppress=False, headerOnly=False, + suppressFeatureGuards=False, moduleName=None, suppressVulkanHeaders=False): + module = cereal.Module( + directory, basename, customAbsDir=customAbsDir, suppress=suppress, implOnly=implOnly, + headerOnly=headerOnly, suppressFeatureGuards=suppressFeatureGuards) + self.addModule(module, moduleName=moduleName) + module.headerPreamble = copyrightHeader + module.headerPreamble += \ + autogeneratedHeaderTemplate % \ + (basename, "(header) generated by %s" % banner_command(sys.argv)) + + module.headerPreamble += "#pragma once\n" + if (not suppressVulkanHeaders): + module.headerPreamble += "#include \n" + module.headerPreamble += '#include "vulkan_gfxstream.h"\n' + module.headerPreamble += extraHeader + '\n' + if useNamespace: + module.headerPreamble += namespaceBegin + + module.implPreamble = copyrightHeader + module.implPreamble += \ + autogeneratedHeaderTemplate % \ + (basename, "(impl) generated by %s" % \ + banner_command(sys.argv)) + if not implOnly: + module.implPreamble += '\n#include "%s.h"' % \ + (basename) + + module.implPreamble += extraImpl + + if useNamespace: + module.implPreamble += namespaceBegin + module.implPostamble += namespaceEnd + module.headerPostamble += namespaceEnd + + def addWrapper(self, moduleType, moduleName, **kwargs): + if moduleName not in self.modules: + print(f'Unknown module: {moduleName}. All known modules are: {", ".join(self.modules)}.') + return + self.wrappers.append( + moduleType( + self.modules[moduleName], + self.typeInfo, **kwargs)) + + def forEachModule(self, func): + for moduleName in self.moduleList: + func(self.modules[moduleName]) + + def forEachWrapper(self, func, supportedWrappers): + for wrapper in self.wrappers: + if supportedWrappers is None: + func(wrapper) + elif type(wrapper) in supportedWrappers: + func(wrapper) + +## Overrides#################################################################### + + def beginFile(self, genOpts): + OutputGenerator.beginFile(self, genOpts) + + write(self.host_cmake_generator(self.hostCMakeCppFiles), + file = self.outFile) + + self.forEachModule(lambda m: m.begin(self.genOpts.directory)) + self.forEachWrapper(lambda w: w.onBegin(), None) + + def endFile(self): + OutputGenerator.endFile(self) + + self.typeInfo.onEnd() + + self.forEachWrapper(lambda w: w.onEnd(), None) + self.forEachModule(lambda m: m.end()) + + def beginFeature(self, interface, emit): + # Start processing in superclass + OutputGenerator.beginFeature(self, interface, emit) + + for supportedFeature in SUPPORTED_FEATURES: + if self.featureName == supportedFeature: + self.featureSupported = True + + if self.featureSupported == False: + return + + self.supportedWrappers = SUPPORTED_WRAPPERS.get(self.featureName) + self.typeInfo.onBeginFeature(self.featureName, self.featureType) + + self.forEachModule( + lambda m: m.appendHeader("#ifdef %s\n" % self.featureName) + if isinstance(m, cereal.Module) and not m.suppressFeatureGuards else None) + self.forEachModule( + lambda m: m.appendImpl("#ifdef %s\n" % self.featureName) + if isinstance(m, cereal.Module) and not m.suppressFeatureGuards else None) + self.forEachWrapper(lambda w: w.onBeginFeature(self.featureName, self.featureType), self.supportedWrappers) + # functable needs to understand the feature type (device vs instance) of each cmd + for features in interface.findall('require'): + for c in features.findall('command'): + self.forEachWrapper(lambda w: w.onFeatureNewCmd(c.get('name')), self.supportedWrappers) + + def endFeature(self): + # Finish processing in superclass + OutputGenerator.endFeature(self) + + if self.featureSupported == False: + return + + self.featureSupported = False + + self.typeInfo.onEndFeature() + + self.forEachModule(lambda m: m.appendHeader("#endif\n") if isinstance( + m, cereal.Module) and not m.suppressFeatureGuards else None) + self.forEachModule(lambda m: m.appendImpl("#endif\n") if isinstance( + m, cereal.Module) and not m.suppressFeatureGuards else None) + self.forEachWrapper(lambda w: w.onEndFeature(), self.supportedWrappers) + + def genType(self, typeinfo: TypeInfo, name, alias): + OutputGenerator.genType(self, typeinfo, name, alias) + + if self.featureSupported == False and name == "int": + self.typeInfo.onGenType(typeinfo, name, alias) + return + + if self.featureSupported == False and name == "int64_t": + self.typeInfo.onGenType(typeinfo, name, alias) + return + + if self.featureSupported == False and name == "double": + self.typeInfo.onGenType(typeinfo, name, alias) + return + + if self.featureSupported == False and name == "VkPresentScalingFlagsEXT": + self.typeInfo.onGenType(typeinfo, name, alias) + return + + if self.featureSupported == False and name == "VkPresentGravityFlagsEXT": + self.typeInfo.onGenType(typeinfo, name, alias) + return + + if self.featureSupported == False: + return + + self.typeInfo.onGenType(typeinfo, name, alias) + self.forEachWrapper(lambda w: w.onGenType(typeinfo, name, alias), self.supportedWrappers) + + def genStruct(self, typeinfo, typeName, alias): + OutputGenerator.genStruct(self, typeinfo, typeName, alias) + if self.featureSupported == False: + return + + self.typeInfo.onGenStruct(typeinfo, typeName, alias) + self.forEachWrapper(lambda w: w.onGenStruct(typeinfo, typeName, alias), self.supportedWrappers) + + def genGroup(self, groupinfo: GroupInfo, groupName, alias = None): + OutputGenerator.genGroup(self, groupinfo, groupName, alias) + if self.featureSupported == False: + return + + self.typeInfo.onGenGroup(groupinfo, groupName, alias) + self.forEachWrapper(lambda w: w.onGenGroup(groupinfo, groupName, alias), self.supportedWrappers) + + def genEnum(self, enuminfo: EnumInfo, name, alias): + OutputGenerator.genEnum(self, enuminfo, name, alias) + if self.featureSupported == False: + return + self.typeInfo.onGenEnum(enuminfo, name, alias) + self.forEachWrapper(lambda w: w.onGenEnum(enuminfo, name, alias), self.supportedWrappers) + + def genCmd(self, cmdinfo, name, alias): + OutputGenerator.genCmd(self, cmdinfo, name, alias) + if self.featureSupported == False: + return + + self.typeInfo.onGenCmd(cmdinfo, name, alias) + self.forEachWrapper(lambda w: w.onGenCmd(cmdinfo, name, alias), self.supportedWrappers)