864 lines
37 KiB
Python
864 lines
37 KiB
Python
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 = """
|
|
|
|
class IOStream;
|
|
|
|
class VkDecoder {
|
|
public:
|
|
VkDecoder();
|
|
~VkDecoder();
|
|
void setForSnapshotLoad(bool forSnapshotLoad);
|
|
size_t decode(void* buf, size_t bufsize, IOStream* stream, uint32_t* seqnoPtr,
|
|
const VkDecoderContext&);
|
|
private:
|
|
class Impl;
|
|
std::unique_ptr<Impl> mImpl;
|
|
};
|
|
"""
|
|
|
|
decoder_impl_preamble ="""
|
|
using android::base::MetricEventBadPacketLength;
|
|
using android::base::MetricEventDuplicateSequenceNum;
|
|
using emugl::vkDispatch;
|
|
|
|
using namespace goldfish_vk;
|
|
|
|
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, uint32_t* seqnoPtr,
|
|
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, uint32_t* seqnoPtr,
|
|
const VkDecoderContext& context) {
|
|
return mImpl->decode(buf, bufsize, stream, seqnoPtr, context);
|
|
}
|
|
|
|
// VkDecoder::Impl::decode to follow
|
|
""" % (VULKAN_STREAM_TYPE, VULKAN_STREAM_TYPE)
|
|
|
|
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 == "vkQueueFlushCommandsGOOGLE" 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)
|
|
|
|
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, logger, 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 logger:
|
|
customParams += ["gfx_logger"]
|
|
if context:
|
|
customParams += ["context"]
|
|
cgen.vkApiCall(api, customPrefix=global_state_prefix, \
|
|
customParameters=customParams, globalStatePrefix=global_state_prefix, \
|
|
checkForDeviceLost=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) __atomic_fetch_add(seqnoPtr, 1, __ATOMIC_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, logger=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, logger, 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_logger(typeInfo, api, cgen):
|
|
emit_decoding(typeInfo, api, cgen, globalWrapped=True, logger=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,
|
|
|
|
"vkBindImageMemory" : 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,
|
|
|
|
"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_logger,
|
|
"vkEndCommandBuffer" : emit_global_state_wrapped_decoding_with_logger,
|
|
"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,
|
|
|
|
# VK_GOOGLE_color_buffer
|
|
"vkRegisterImageColorBufferGOOGLE" : emit_global_state_wrapped_decoding,
|
|
"vkRegisterBufferColorBufferGOOGLE" : 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_logger,
|
|
"vkEndCommandBufferAsyncGOOGLE" : emit_global_state_wrapped_decoding_with_logger,
|
|
"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,
|
|
"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, uint32_t* seqnoPtr,
|
|
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("""
|
|
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 (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 watchdog =
|
|
WATCHDOG_BUILDER(healthMonitor,
|
|
"RenderThread seqno loop")
|
|
.setHangType(EventHangMetadata::HangType::kRenderThread)
|
|
/* Data gathered if this hangs*/
|
|
.setOnHangCallback([=]() {
|
|
auto annotations = std::make_unique<EventHangMetadata::HangAnnotations>();
|
|
annotations->insert({{"seqno", std::to_string(seqno)},
|
|
{"seqnoPtr", std::to_string(__atomic_load_n(
|
|
seqnoPtr, __ATOMIC_SEQ_CST))},
|
|
{"opcode", std::to_string(opcode)},
|
|
{"buffer_length", std::to_string(len)}});
|
|
if (processName) {
|
|
annotations->insert(
|
|
{{"renderthread_guest_process", std::string(processName)}});
|
|
}
|
|
return std::move(annotations);
|
|
})
|
|
.build();
|
|
while ((seqno - __atomic_load_n(seqnoPtr, __ATOMIC_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.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())
|