diff options
Diffstat (limited to 'codegen/vulkan/scripts/cereal/decoder.py')
-rw-r--r-- | codegen/vulkan/scripts/cereal/decoder.py | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/codegen/vulkan/scripts/cereal/decoder.py b/codegen/vulkan/scripts/cereal/decoder.py new file mode 100644 index 00000000..e082db0c --- /dev/null +++ b/codegen/vulkan/scripts/cereal/decoder.py @@ -0,0 +1,912 @@ +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<Impl> 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<uint32_t> 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, + 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, + 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, + 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<void()> 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.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 readStream = 0") + cgen.stmt("memcpy(&readStream, *readStreamPtrPtr, sizeof(uint64_t)); *readStreamPtrPtr += 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.stmt("uint8_t* targetRange = hostPtr + offset") + cgen.stmt("memcpy(targetRange, *readStreamPtrPtr, readStream); *readStreamPtrPtr += 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, + "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, + "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, + + # 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, + "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, +} + +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<EventHangMetadata::HangAnnotations> executionData = + std::make_unique<EventHangMetadata::HangAnnotations>(); + 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<uint32_t>* seqnoPtr = processResources->getSequenceNumberPtr(); + + 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<EventHangMetadata::HangAnnotations>(*executionData)) + /* Data gathered if this hangs*/ + .setOnHangCallback([=]() { + auto annotations = std::make_unique<EventHangMetadata::HangAnnotations>(); + 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) |