From 727f450be245d1096a13944f9732d1c7749ce199 Mon Sep 17 00:00:00 2001 From: Remi Palandri Date: Tue, 13 Jan 2026 12:00:08 +0100 Subject: [PATCH] add D3D11 support for annotations on immediate context only --- renderdoc/driver/d3d11/d3d11_common.cpp | 4 + renderdoc/driver/d3d11/d3d11_common.h | 1 + renderdoc/driver/d3d11/d3d11_context.cpp | 60 ++++++++- renderdoc/driver/d3d11/d3d11_context.h | 25 ++++ renderdoc/driver/d3d11/d3d11_context_wrap.cpp | 114 ++++++++++++++++++ renderdoc/driver/d3d11/d3d11_device.cpp | 30 +++++ renderdoc/driver/d3d11/d3d11_device.h | 12 +- renderdoc/driver/d3d11/d3d11_stringise.cpp | 4 +- 8 files changed, 238 insertions(+), 12 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_common.cpp b/renderdoc/driver/d3d11/d3d11_common.cpp index aab1660cb..a5ed07133 100644 --- a/renderdoc/driver/d3d11/d3d11_common.cpp +++ b/renderdoc/driver/d3d11/d3d11_common.cpp @@ -528,6 +528,10 @@ bool D3D11InitParams::IsSupportedVersion(uint64_t ver) if(ver == 0x12) return true; + // 0x13 -> 0x14 - added serialised annotations + if(ver == 0x12) + return true; + return false; } diff --git a/renderdoc/driver/d3d11/d3d11_common.h b/renderdoc/driver/d3d11/d3d11_common.h index dfb48e197..a2707d2ac 100644 --- a/renderdoc/driver/d3d11/d3d11_common.h +++ b/renderdoc/driver/d3d11/d3d11_common.h @@ -403,6 +403,7 @@ enum class D3D11Chunk : uint32_t OpenSharedResource1, OpenSharedResourceByName, SetShaderExtUAV, + SetCommandAnnotation, Max, }; diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 4646cb673..1c08deecc 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -236,6 +236,17 @@ WrappedID3D11DeviceContext::~WrappedID3D11DeviceContext() SAFE_DELETE(m_DescriptorStore); + // Clean up annotation data + for(auto it = m_Annotations.begin(); it != m_Annotations.end(); ++it) + delete it->second; + m_Annotations.clear(); + + SAFE_DELETE(m_RootAnnotation); + + for(SDObject *obj : m_EventAnnotations) + delete obj; + m_EventAnnotations.clear(); + SAFE_DELETE(m_FrameReader); SAFE_RELEASE(m_WrappedVideo.m_pReal); @@ -451,6 +462,35 @@ bool WrappedID3D11DeviceContext::Serialise_BeginCaptureFrame(SerialiserType &ser } } + // Serialize object annotations + if(ser.VersionAtLeast(0x14)) + { + SCOPED_LOCK(m_AnnotationsLock); + + SERIALISE_ELEMENT_LOCAL(numAnnotations, uint32_t(m_Annotations.size())); + + auto it = m_Annotations.begin(); + for(uint32_t i = 0; i < numAnnotations; i++) + { + SERIALISE_ELEMENT_LOCAL(id, it->first); + SDObject *annotation = it->second; + if(ser.IsReading()) + annotation = new SDObject(""_lit, ""_lit); // will be overwritten below + ser.Serialise("annotation"_lit, *annotation); + + if(ser.IsReading() && IsLoading(m_State)) + { + m_Annotations[id] = annotation; + m_pDevice->GetReplay()->GetResourceDesc(id).annotations = annotation; + } + + ++it; + } + + if(numAnnotations > 0) + m_pDevice->GetReplay()->WriteFrameRecord().frameInfo.containsAnnotations = true; + } + return true; } @@ -895,6 +935,11 @@ bool WrappedID3D11DeviceContext::ProcessChunk(ReadSerialiser &ser, D3D11Chunk ch case D3D11Chunk::SetMarker: ret = Serialise_SetMarker(ser, 0, L""); break; case D3D11Chunk::PopMarker: ret = Serialise_PopMarker(ser); break; + case D3D11Chunk::SetCommandAnnotation: + ret = Serialise_SetCommandAnnotation(ser, rdcstr(), eRENDERDOC_AnnotationMax, 0, + RENDERDOC_AnnotationValue()); + break; + case D3D11Chunk::DiscardResource: ret = Serialise_DiscardResource(ser, NULL); break; case D3D11Chunk::DiscardView: ret = Serialise_DiscardView(ser, NULL); break; case D3D11Chunk::DiscardView1: ret = Serialise_DiscardView1(ser, NULL, NULL, 0); break; @@ -1014,7 +1059,8 @@ bool WrappedID3D11DeviceContext::ProcessChunk(ReadSerialiser &ser, D3D11Chunk ch m_ActionStack.pop_back(); } - if(!m_AddedAction) + // annotations don't add events + if(!m_AddedAction && chunk != D3D11Chunk::SetCommandAnnotation) AddEvent(); } @@ -1212,6 +1258,13 @@ void WrappedID3D11DeviceContext::AddEvent() } } + // Apply current annotation state to this event + if(m_RootAnnotation) + { + apievent.annotations = m_RootAnnotation->Duplicate(); + m_EventAnnotations.push_back(apievent.annotations); + } + m_CurEvents.push_back(apievent); if(IsLoading(m_State)) @@ -1382,7 +1435,10 @@ RDResult WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t s break; m_LastChunk = chunktype; - m_CurEventID++; + + // annotations do not produce events + if(chunktype != D3D11Chunk::SetCommandAnnotation) + m_CurEventID++; } if(IsLoading(m_State)) diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index f67bb9d3a..0b63abf08 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -227,6 +227,12 @@ private: rdcarray m_AnnotationQueue; Threading::CriticalSection m_AnnotLock; + // Object and command annotation support + Threading::CriticalSection m_AnnotationsLock; + std::unordered_map m_Annotations; // Object annotations by ResourceId + SDObject *m_RootAnnotation = NULL; // Root for event annotation state + rdcarray m_EventAnnotations; // Track allocations for cleanup + uint64_t m_TimeBase = 0; double m_TimeFrequency = 1.0f; SDFile *m_StructuredFile = NULL; @@ -290,12 +296,25 @@ private: SERIALISED_ID3D11CONTEXT_MARKER_FUNCTIONS(); +#define SERIALISED_ID3D11CONTEXT_ANNOTATION_FUNCTIONS() \ + IMPLEMENT_FUNCTION_SERIALISED(bool, SetCommandAnnotation, rdcstr key, \ + RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth, \ + RENDERDOC_AnnotationValue value); + + SERIALISED_ID3D11CONTEXT_ANNOTATION_FUNCTIONS(); + public: ALLOCATE_WITH_WRAPPED_POOL(WrappedID3D11DeviceContext); WrappedID3D11DeviceContext(WrappedID3D11Device *realDevice, ID3D11DeviceContext *context); virtual ~WrappedID3D11DeviceContext(); + void RemoveAnnotations(ResourceId id) + { + SCOPED_LOCK(m_AnnotationsLock); + m_Annotations.erase(id); + } + void SetReplayResourceID(ResourceId id) { m_ResourceID = id; } void VerifyState(); @@ -358,6 +377,12 @@ public: void IntAddRef(); void IntRelease(); + // annotations functions in DeviceContext (immediate only) + uint32_t SetCommandAnnotation(const char *key, RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, const RENDERDOC_AnnotationValue *value); + uint32_t SetObjectAnnotation(void *object, const char *key, RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, const RENDERDOC_AnnotationValue *value); + ////////////////////////////// // implement IUnknown ULONG STDMETHODCALLTYPE AddRef(); diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index 7edd201cb..f3bb5242c 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -29,6 +29,7 @@ #include "tinyfiledialogs/tinyfiledialogs.h" #include "d3d11_debug.h" #include "d3d11_renderstate.h" +#include "d3d11_replay.h" #include "d3d11_resources.h" #ifndef DXGI_ERROR_INVALID_CALL @@ -188,6 +189,118 @@ int WrappedID3D11DeviceContext::PopMarker() return --m_MarkerIndentLevel; } +template +bool WrappedID3D11DeviceContext::Serialise_SetCommandAnnotation(SerialiserType &ser, rdcstr key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + RENDERDOC_AnnotationValue value) +{ + SERIALISE_ELEMENT(key); + SERIALISE_ELEMENT(valueType); + ser.SetStructArg(valueType); + SERIALISE_ELEMENT(valueVectorWidth); + SERIALISE_ELEMENT(value); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + if(IsLoading(m_State)) + { + if(!m_RootAnnotation) + m_RootAnnotation = new SDObject("Event Annotations"_lit, "Event Annotations"_lit); + + SDObject *root = m_RootAnnotation; + + if(valueType == eRENDERDOC_Empty) + { + root->EraseChildByKeyPath(key); + } + else + { + WriteAnnotation(root->CreateChildByKeyPath(key), valueType, valueVectorWidth, value); + } + + m_pDevice->GetReplay()->WriteFrameRecord().frameInfo.containsAnnotations = true; + } + } + + return true; +} + +uint32_t WrappedID3D11DeviceContext::SetCommandAnnotation(const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + SERIALISE_TIME_CALL(); + + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + GET_SERIALISER.SetActionChunk(); + SCOPED_SERIALISE_CHUNK(D3D11Chunk::SetCommandAnnotation); + SERIALISE_ELEMENT(m_ResourceID).Named("Context"_lit).TypedAs("ID3D11DeviceContext *"_lit); + + RENDERDOC_AnnotationValue val = value ? *value : RENDERDOC_AnnotationValue(); + + if(valueType == eRENDERDOC_APIObject && val.apiObject) + { + ResourceId id = GetIDForDeviceChild((ID3D11DeviceChild *)val.apiObject); + RDCCOMPILE_ASSERT(sizeof(val.uint64) == sizeof(id), "ResourceId isn't 64-bit!"); + memcpy(&val.uint64, &id, sizeof(id)); + } + + Serialise_SetCommandAnnotation(GET_SERIALISER, key, valueType, valueVectorWidth, val); + + m_ContextRecord->AddChunk(scope.Get()); + } + + return 0; +} + +uint32_t WrappedID3D11DeviceContext::SetObjectAnnotation(void *object, const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + ResourceId id = GetIDForDeviceChild((ID3D11DeviceChild *)object); + + if(id != ResourceId()) + { + RENDERDOC_AnnotationValue val = value ? *value : RENDERDOC_AnnotationValue(); + + // Convert API object references to ResourceId + if(valueType == eRENDERDOC_APIObject && val.apiObject) + { + ResourceId valId = GetIDForDeviceChild((ID3D11DeviceChild *)val.apiObject); + RDCCOMPILE_ASSERT(sizeof(val.uint64) == sizeof(valId), "ResourceId isn't 64-bit!"); + memcpy(&val.uint64, &valId, sizeof(valId)); + } + + SDObject *root = NULL; + { + SCOPED_LOCK(m_AnnotationsLock); + root = m_Annotations[id]; + if(!root) + root = m_Annotations[id] = new SDObject("Object Annotations"_lit, "Object Annotations"_lit); + } + + if(valueType == eRENDERDOC_Empty) + { + root->EraseChildByKeyPath(key); + } + else + { + WriteAnnotation(root->CreateChildByKeyPath(key), valueType, valueVectorWidth, val); + } + + return 0; + } + + return 2; +} + void WrappedID3D11DeviceContext::ThreadSafe_SetMarker(uint32_t col, const wchar_t *name) { Annotation annot; @@ -8153,3 +8266,4 @@ void WrappedID3D11DeviceContext::Unmap(ID3D11Resource *pResource, UINT Subresour SERIALISED_ID3D11CONTEXT_FUNCTIONS(); SERIALISED_ID3D11CONTEXT_MARKER_FUNCTIONS(); +SERIALISED_ID3D11CONTEXT_ANNOTATION_FUNCTIONS(); diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 2e70c07f4..6a3a52bae 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -1230,6 +1230,7 @@ bool WrappedID3D11Device::ProcessChunk(ReadSerialiser &ser, D3D11Chunk context) case D3D11Chunk::PostExecuteCommandList: case D3D11Chunk::PostFinishCommandListSet: case D3D11Chunk::SwapDeviceContextState: + case D3D11Chunk::SetCommandAnnotation: case D3D11Chunk::SwapchainPresent: return m_pImmediateContext->ProcessChunk(ser, context); // no explicit default so that we have compiler warnings if a chunk isn't explicitly handled. @@ -1947,6 +1948,9 @@ void WrappedID3D11Device::DestroyDeadObject(ID3D11DeviceChild *child) { ResourceId id = wrapped->GetResourceID(); + if(m_pImmediateContext) + m_pImmediateContext->RemoveAnnotations(id); + // clean up book-keeping rm->RemoveWrapper(wrapped->GetReal()); rm->ReleaseResource(id); @@ -1998,6 +2002,32 @@ int WrappedID3D11Device::EndEvent() return m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_EndEvent(); } +uint32_t WrappedID3D11Device::SetObjectAnnotation(void *object, const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + return m_pImmediateContext->SetObjectAnnotation(object, key, valueType, valueVectorWidth, value); +} + +uint32_t WrappedID3D11Device::SetCommandAnnotation(void *queueOrCommandBuffer, const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + if(queueOrCommandBuffer != NULL && queueOrCommandBuffer != m_pImmediateContext) + return 2; + + // D3D11 doesn't have queues or command buffers, so we accept NULL + // and treat all command annotations as immediate/global + if(IsActiveCapturing(m_State)) + { + return m_pImmediateContext->SetCommandAnnotation(key, valueType, valueVectorWidth, value); + } + + return 0; +} + void WrappedID3D11Device::StartFrameCapture(DeviceOwnedWindow devWnd) { SCOPED_LOCK(m_D3DLock); diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 65b433e48..2e62885ed 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -82,7 +82,7 @@ struct D3D11InitParams uint32_t VendorUAV = ~0U; // check if a frame capture section version is supported - static const uint64_t CurrentVersion = 0x13; + static const uint64_t CurrentVersion = 0x14; static bool IsSupportedVersion(uint64_t ver); }; @@ -722,16 +722,10 @@ public: bool EndFrameCapture(DeviceOwnedWindow devWnd); bool DiscardFrameCapture(DeviceOwnedWindow devWnd); uint32_t SetObjectAnnotation(void *object, const char *key, RENDERDOC_AnnotationType valueType, - uint32_t valueVectorWidth, const RENDERDOC_AnnotationValue *value) - { - return 2; - } + uint32_t valueVectorWidth, const RENDERDOC_AnnotationValue *value); uint32_t SetCommandAnnotation(void *queueOrCommandBuffer, const char *key, RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth, - const RENDERDOC_AnnotationValue *value) - { - return 2; - } + const RENDERDOC_AnnotationValue *value); ID3DUserDefinedAnnotation *GetAnnotations() { return m_RealAnnotations; } ID3D11InfoQueue *GetInfoQueue() { return m_pInfoQueue; } diff --git a/renderdoc/driver/d3d11/d3d11_stringise.cpp b/renderdoc/driver/d3d11/d3d11_stringise.cpp index 201db65e4..d4ec80e62 100644 --- a/renderdoc/driver/d3d11/d3d11_stringise.cpp +++ b/renderdoc/driver/d3d11/d3d11_stringise.cpp @@ -59,7 +59,7 @@ rdcstr DoStringise(const D3D11ResourceType &el) template <> rdcstr DoStringise(const D3D11Chunk &el) { - RDCCOMPILE_ASSERT((uint32_t)D3D11Chunk::Max == 1131, "Chunks changed without updating names"); + RDCCOMPILE_ASSERT((uint32_t)D3D11Chunk::Max == 1132, "Chunks changed without updating names"); BEGIN_ENUM_STRINGISE(D3D11Chunk) { @@ -213,6 +213,8 @@ rdcstr DoStringise(const D3D11Chunk &el) STRINGISE_ENUM_CLASS_NAMED(OpenSharedResource1, "ID3D11Device1::OpenSharedResource1"); STRINGISE_ENUM_CLASS_NAMED(OpenSharedResourceByName, "ID3D11Device1::OpenSharedResourceByName"); STRINGISE_ENUM_CLASS_NAMED(SetShaderExtUAV, "VendorExtension::SetExtensionUAVSlot"); + STRINGISE_ENUM_CLASS_NAMED(SetCommandAnnotation, "Internal::SetCommandAnnotation"); + STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk"); } END_ENUM_STRINGISE()