| 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, |
| "vkCmdPipelineBarrier2": 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, |
| "vkCmdBeginRenderPass" : emit_global_state_wrapped_decoding, |
| "vkCmdBeginRenderPass2" : emit_global_state_wrapped_decoding, |
| "vkCmdBeginRenderPass2KHR" : 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()) |