diff options
Diffstat (limited to 'codegen/vulkan/scripts/cereal/subdecode.py')
-rw-r--r-- | codegen/vulkan/scripts/cereal/subdecode.py | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/codegen/vulkan/scripts/cereal/subdecode.py b/codegen/vulkan/scripts/cereal/subdecode.py new file mode 100644 index 00000000..b77cad77 --- /dev/null +++ b/codegen/vulkan/scripts/cereal/subdecode.py @@ -0,0 +1,395 @@ +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, + 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, + 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, + 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, + 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, + "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()) |