summaryrefslogtreecommitdiff
path: root/codegen/vulkan/scripts/cereal/subdecode.py
diff options
context:
space:
mode:
Diffstat (limited to 'codegen/vulkan/scripts/cereal/subdecode.py')
-rw-r--r--codegen/vulkan/scripts/cereal/subdecode.py395
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())