From 14d74e4dd36facb81f2d67cf92bd663b9cbe87ca Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Sun, 4 Sep 2022 06:49:42 +0100 Subject: [PATCH] Improve tracking of MTLDrawables (swapchains) Serialize the presented texture (presentedImage) as part of "presentDrawable" serialization. Use the present texture resource ID in the action name ie. "presentDrawable(Texture 23219)" Track the most recently presented image and use that in SystemChunk::CaptureEnd if the PresentedImage serialized data is empty resource. Track the CAMetalDrawable's created via the hooked CAMetalLayer::nextDrawable() method. Implements TODO in WrappedMTLCommandBuffer::presentDrawable "remove the (CA::MetalDrawable*) cast. Associate created texture and layer in hooked nextDrawable with MTL::Drawable*" --- .../driver/metal/metal_command_buffer.cpp | 53 ++++++++++++------- renderdoc/driver/metal/metal_command_buffer.h | 4 +- renderdoc/driver/metal/metal_core.cpp | 34 ++++++++++-- renderdoc/driver/metal/metal_device.cpp | 13 +++-- renderdoc/driver/metal/metal_device.h | 17 ++++++ 5 files changed, 94 insertions(+), 27 deletions(-) diff --git a/renderdoc/driver/metal/metal_command_buffer.cpp b/renderdoc/driver/metal/metal_command_buffer.cpp index f3b7e22cb..5ade3091e 100644 --- a/renderdoc/driver/metal/metal_command_buffer.cpp +++ b/renderdoc/driver/metal/metal_command_buffer.cpp @@ -149,42 +149,59 @@ WrappedMTLRenderCommandEncoder *WrappedMTLCommandBuffer::renderCommandEncoderWit } template -bool WrappedMTLCommandBuffer::Serialise_presentDrawable(SerialiserType &ser, MTL::Drawable *drawable) +bool WrappedMTLCommandBuffer::Serialise_presentDrawable(SerialiserType &ser, + WrappedMTLTexture *presentedImage) { SERIALISE_ELEMENT_LOCAL(CommandBuffer, this); + SERIALISE_ELEMENT(presentedImage).Important(); SERIALISE_CHECK_READ_ERRORS(); // TODO: implement RD MTL replay if(IsReplayingAndReading()) { + if(IsLoading(m_State)) + { + AddEvent(); + + ActionDescription action; + ResourceId presentedImageId = GetResourceManager()->GetOriginalID(GetResID(presentedImage)); + action.customName = StringFormat::Fmt("presentDrawable(%s)", ToStr(presentedImageId).c_str()); + action.flags |= ActionFlags::Present; + action.copyDestination = presentedImageId; + m_Device->SetLastPresentedIamge(presentedImageId); + AddAction(action); + } } return true; } void WrappedMTLCommandBuffer::presentDrawable(MTL::Drawable *drawable) { - // TODO: remove the (CA::MetalDrawable*) cast. Associate created texture - // in hooked nextDrawable with MTL::Drawable* and CA::MetalLayer* - CA::MetalDrawable *mtlDrawable = (CA::MetalDrawable *)(drawable); - // To avoid metal assert about accessing drawable texture after calling present - MTL::Texture *mtlBackBuffer = mtlDrawable->texture(); - SERIALISE_TIME_CALL(Unwrap(this)->presentDrawable(drawable)); if(IsCaptureMode(m_State)) { - Chunk *chunk = NULL; + MetalDrawableInfo info = m_Device->UnregisterDrawableInfo(drawable); + WrappedMTLTexture *presentedImage = info.texture; + if(presentedImage) { - CACHE_THREAD_SERIALISER(); - SCOPED_SERIALISE_CHUNK(MetalChunk::MTLCommandBuffer_presentDrawable); - Serialise_presentDrawable(ser, drawable); - chunk = scope.Get(); + Chunk *chunk = NULL; + { + CACHE_THREAD_SERIALISER(); + SCOPED_SERIALISE_CHUNK(MetalChunk::MTLCommandBuffer_presentDrawable); + Serialise_presentDrawable(ser, presentedImage); + chunk = scope.Get(); + } + MetalResourceRecord *bufferRecord = GetRecord(this); + bufferRecord->AddChunk(chunk); + bufferRecord->cmdInfo->presented = true; + bufferRecord->cmdInfo->outputLayer = info.mtlLayer; + bufferRecord->cmdInfo->backBuffer = presentedImage; + } + else + { + RDCERR("Ignoring presentDrawable on untracked MTLDrawable"); } - MetalResourceRecord *bufferRecord = GetRecord(this); - bufferRecord->AddChunk(chunk); - bufferRecord->cmdInfo->presented = true; - bufferRecord->cmdInfo->outputLayer = mtlDrawable->layer(); - bufferRecord->cmdInfo->backBuffer = GetWrapped(mtlBackBuffer); } else { @@ -309,7 +326,7 @@ INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLCommandBuffer, renderCommandEncoderWithDescriptor, RDMTL::RenderPassDescriptor &descriptor); INSTANTIATE_FUNCTION_SERIALISED(WrappedMTLCommandBuffer, void, presentDrawable, - MTL::Drawable *drawable); + WrappedMTLTexture *presentedImage); INSTANTIATE_FUNCTION_SERIALISED(WrappedMTLCommandBuffer, void, commit); INSTANTIATE_FUNCTION_SERIALISED(WrappedMTLCommandBuffer, void, enqueue); INSTANTIATE_FUNCTION_SERIALISED(WrappedMTLCommandBuffer, void, waitUntilCompleted); diff --git a/renderdoc/driver/metal/metal_command_buffer.h b/renderdoc/driver/metal/metal_command_buffer.h index 6ad1b121e..a757113ed 100644 --- a/renderdoc/driver/metal/metal_command_buffer.h +++ b/renderdoc/driver/metal/metal_command_buffer.h @@ -41,7 +41,9 @@ public: DECLARE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLRenderCommandEncoder *, renderCommandEncoderWithDescriptor, RDMTL::RenderPassDescriptor &descriptor); - DECLARE_FUNCTION_SERIALISED(void, presentDrawable, MTL::Drawable *drawable); + void presentDrawable(MTL::Drawable *drawable); + template + bool Serialise_presentDrawable(SerialiserType &ser, WrappedMTLTexture *presentedImage); DECLARE_FUNCTION_SERIALISED(void, commit); DECLARE_FUNCTION_SERIALISED(void, enqueue); DECLARE_FUNCTION_SERIALISED(void, waitUntilCompleted); diff --git a/renderdoc/driver/metal/metal_core.cpp b/renderdoc/driver/metal/metal_core.cpp index 31126f263..3ad78646c 100644 --- a/renderdoc/driver/metal/metal_core.cpp +++ b/renderdoc/driver/metal/metal_core.cpp @@ -442,8 +442,8 @@ bool WrappedMTLDevice::ProcessChunk(ReadSerialiser &ser, MetalChunk chunk) SERIALISE_CHECK_READ_ERRORS(); - // TODO: handle null PresentedImage - // TODO: Track most recently presented texture in Serialise_presentDrawable + if(PresentedImage != ResourceId()) + m_LastPresentedImage = PresentedImage; if(IsLoading(m_State)) { @@ -452,7 +452,7 @@ bool WrappedMTLDevice::ProcessChunk(ReadSerialiser &ser, MetalChunk chunk) ActionDescription action; action.customName = "End of Capture"; action.flags |= ActionFlags::Present; - action.copyDestination = PresentedImage; + action.copyDestination = m_LastPresentedImage; AddAction(action); } return true; @@ -1029,6 +1029,34 @@ void WrappedMTLDevice::UnregisterMetalLayer(CA::MetalLayer *mtlLayer) RenderDoc::Inst().RemoveFrameCapturer(devWnd); } +void WrappedMTLDevice::RegisterDrawableInfo(CA::MetalDrawable *caMtlDrawable) +{ + MetalDrawableInfo drawableInfo; + drawableInfo.mtlLayer = caMtlDrawable->layer(); + drawableInfo.texture = GetWrapped(caMtlDrawable->texture()); + SCOPED_LOCK(m_CaptureDrawablesLock); + RDCASSERTEQUAL(m_CaptureDrawableInfos.find(caMtlDrawable), m_CaptureDrawableInfos.end()); + m_CaptureDrawableInfos[caMtlDrawable] = drawableInfo; +} + +MetalDrawableInfo WrappedMTLDevice::UnregisterDrawableInfo(MTL::Drawable *mtlDrawable) +{ + MetalDrawableInfo drawableInfo; + { + SCOPED_LOCK(m_CaptureDrawablesLock); + auto it = m_CaptureDrawableInfos.find(mtlDrawable); + if(it != m_CaptureDrawableInfos.end()) + { + drawableInfo = it->second; + m_CaptureDrawableInfos.erase(it); + return drawableInfo; + } + } + drawableInfo.mtlLayer = NULL; + drawableInfo.texture = NULL; + return drawableInfo; +} + MetalInitParams::MetalInitParams() { memset(this, 0, sizeof(MetalInitParams)); diff --git a/renderdoc/driver/metal/metal_device.cpp b/renderdoc/driver/metal/metal_device.cpp index d0230a643..30119d66b 100644 --- a/renderdoc/driver/metal/metal_device.cpp +++ b/renderdoc/driver/metal/metal_device.cpp @@ -97,20 +97,23 @@ WrappedMTLDevice::WrappedMTLDevice(MTL::Device *realMTLDevice, ResourceId objId) IMP WrappedMTLDevice::g_real_CAMetalLayer_nextDrawable; uint64_t WrappedMTLDevice::g_nextDrawableTLSSlot; -MTL::Drawable *hooked_CAMetalLayer_nextDrawable(id self, SEL _cmd) +CA::MetalDrawable *hooked_CAMetalLayer_nextDrawable(id self, SEL _cmd) { CA::MetalLayer *mtlLayer = (CA::MetalLayer *)self; MTL::Device *mtlDevice = mtlLayer->device(); + WrappedMTLDevice *device = GetWrapped(mtlDevice); RDCASSERT(object_getClass(mtlDevice) == objc_getClass("ObjCBridgeMTLDevice")); - GetWrapped(mtlDevice)->RegisterMetalLayer(mtlLayer); + device->RegisterMetalLayer(mtlLayer); mtlLayer->setFramebufferOnly(false); RDCASSERTEQUAL(Threading::GetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot), 0); Threading::SetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot, (void *)(uintptr_t) true); - MTL::Drawable *drawable = - ((MTL::Drawable * (*)(id, SEL))WrappedMTLDevice::g_real_CAMetalLayer_nextDrawable)(self, _cmd); + CA::MetalDrawable *caMtlDrawable = + ((CA::MetalDrawable * (*)(id, SEL))WrappedMTLDevice::g_real_CAMetalLayer_nextDrawable)(self, + _cmd); + device->RegisterDrawableInfo(caMtlDrawable); Threading::SetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot, (void *)(uintptr_t) false); - return drawable; + return caMtlDrawable; } void WrappedMTLDevice::MTLHookObjcMethods() diff --git a/renderdoc/driver/metal/metal_device.h b/renderdoc/driver/metal/metal_device.h index f71a821df..c38480018 100644 --- a/renderdoc/driver/metal/metal_device.h +++ b/renderdoc/driver/metal/metal_device.h @@ -31,6 +31,12 @@ class WrappedMTLDevice; class MetalReplay; +struct MetalDrawableInfo +{ + CA::MetalLayer *mtlLayer; + WrappedMTLTexture *texture; +}; + class MetalCapturer : public IFrameCapturer { public: @@ -131,6 +137,9 @@ public: void RegisterMetalLayer(CA::MetalLayer *mtlLayer); void UnregisterMetalLayer(CA::MetalLayer *mtlLayer); + void RegisterDrawableInfo(CA::MetalDrawable *caMtlDrawable); + MetalDrawableInfo UnregisterDrawableInfo(MTL::Drawable *mtlDrawable); + void AddEvent(); void AddAction(const ActionDescription &a); @@ -143,6 +152,11 @@ public: DerivedResource(GetResID(parent), child); } + void SetLastPresentedIamge(ResourceId lastPresentedImage) + { + m_LastPresentedImage = lastPresentedImage; + } + enum { TypeEnum = eResDevice @@ -177,6 +191,7 @@ private: MTL::ResourceOptions options); MetalResourceManager *m_ResourceManager = NULL; + ResourceId m_LastPresentedImage; // Dummy objects used for serialisation replay WrappedMTLBuffer *m_DummyBuffer = NULL; @@ -194,6 +209,8 @@ private: Threading::CriticalSection m_CaptureOutputLayersLock; std::unordered_set m_CaptureOutputLayers; WrappedMTLTexture *m_CapturedBackbuffer = NULL; + Threading::CriticalSection m_CaptureDrawablesLock; + rdcflatmap m_CaptureDrawableInfos; CaptureState m_State; bool m_AppControlledCapture = false;