diff --git a/renderdoc/api/app/renderdoc_app.h b/renderdoc/api/app/renderdoc_app.h index 0fd830750..3cee3bd48 100644 --- a/renderdoc/api/app/renderdoc_app.h +++ b/renderdoc/api/app/renderdoc_app.h @@ -622,8 +622,30 @@ typedef union RENDERDOC_AnnotationValue void *apiObject; } RENDERDOC_AnnotationValue; -// simple C++ helper to avoid the need for a temporary object to pass a simple value via the function +// a struct for specifying a GL object, as we don't have pointers we can use so instead we specify a +// pointer to this struct giving both the type and the name +typedef struct RENDERDOC_GLResourceReference +{ + // this is the same GLenum identifier as passed to glObjectLabel + uint32_t identifier; + uint32_t name; +} GLResourceReference; + +// simple C++ helpers to avoid the need for a temporary objects for value passing and GL object specification #ifdef __cplusplus +struct RDGLObjectHelper +{ + RENDERDOC_GLResourceReference gl; + + RDGLObjectHelper(uint32_t identifier, uint32_t name) + { + gl.identifier = identifier; + gl.name = name; + } + + operator RENDERDOC_GLResourceReference *() { return ≷ } +}; + struct RDAnnotationHelper { RENDERDOC_AnnotationValue val; diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index 67cbfa80b..76318443e 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -1266,6 +1266,10 @@ bool GLInitParams::IsSupportedVersion(uint64_t ver) if(ver == 0x22) return true; + // 0x23 -> 0x24 - added serialised annotations + if(ver == 0x23) + return true; + return false; } diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 968511985..12f626c56 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -2249,6 +2249,8 @@ enum class GLChunk : uint32_t glFlushMappedBufferRangeEXT, glClipControlEXT, + SetCommandAnnotation, + Max, }; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 36e74a2d5..ae008be61 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -947,6 +947,17 @@ WrappedOpenGL::~WrappedOpenGL() if(m_IndirectBuffer) GL.glDeleteBuffers(1, &m_IndirectBuffer); + // 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(); + m_ArrayMS.Destroy(); SAFE_DELETE(m_FrameReader); @@ -3120,6 +3131,35 @@ bool WrappedOpenGL::Serialise_BeginCaptureFrame(SerialiserType &ser) SERIALISE_ELEMENT(state).Unimportant(); + // Serialize object annotations + if(ser.VersionAtLeast(0x24)) + { + 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; + GetReplay()->GetResourceDesc(id).annotations = annotation; + } + + ++it; + } + + if(numAnnotations > 0) + GetReplay()->WriteFrameRecord().frameInfo.containsAnnotations = true; + } + SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) @@ -4747,6 +4787,10 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) return Serialise_BeginCaptureFrame(ser); } + case GLChunk::SetCommandAnnotation: + return Serialise_SetCommandAnnotation(ser, rdcstr(), eRENDERDOC_AnnotationMax, 0, + RENDERDOC_AnnotationValue()); + case GLChunk::ContextConfiguration: return Serialise_ContextConfiguration(ser, NULL); case GLChunk::glIndirectSubCommand: @@ -5294,7 +5338,10 @@ RDResult WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t startEv break; m_LastChunk = chunktype; - m_CurEventID++; + + // annotations do not produce events + if(chunktype != GLChunk::SetCommandAnnotation) + m_CurEventID++; } // swap the structure back now that we've accumulated the frame as well. @@ -5400,7 +5447,8 @@ bool WrappedOpenGL::ContextProcessChunk(ReadSerialiser &ser, GLChunk chunk) default: break; } - if(!m_AddedAction) + // annotations don't add events + if(!m_AddedAction && chunk != GLChunk::SetCommandAnnotation) AddEvent(); } @@ -5757,6 +5805,13 @@ void WrappedOpenGL::AddEvent() apievent.chunkIndex = uint32_t(m_StructuredFile->chunks.size() - 1); + // 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)) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 375d58b9f..8db7bb63d 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -59,7 +59,7 @@ struct GLInitParams rdcstr renderer, version; // check if a frame capture section version is supported - static const uint64_t CurrentVersion = 0x23; + static const uint64_t CurrentVersion = 0x24; static bool IsSupportedVersion(uint64_t ver); }; @@ -186,6 +186,12 @@ private: void *m_LastCtx; int m_ImplicitThreadSwitches = 0; + // 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 + GLContextTLSData m_EmptyTLSData; uint64_t m_CurCtxDataTLS; rdcarray m_CtxDataVector; @@ -574,6 +580,12 @@ private: rdcarray m_QueuedInitialFetches; rdcarray m_QueuedReleases; + void RemoveAnnotations(ResourceId id) + { + SCOPED_LOCK(m_AnnotationsLock); + m_Annotations.erase(id); + } + void QueuePrepareInitialState(GLResource res); void QueueResourceRelease(GLResource res); void CheckQueuedInitialFetches(void *ctx); @@ -705,17 +717,17 @@ public: void StartFrameCapture(DeviceOwnedWindow devWnd); bool EndFrameCapture(DeviceOwnedWindow devWnd); bool DiscardFrameCapture(DeviceOwnedWindow devWnd); + + template + bool Serialise_SetCommandAnnotation(SerialiserType &ser, rdcstr key, + RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth, + RENDERDOC_AnnotationValue value); + 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); // map with key being mip level, value being stored data typedef std::map CompressedDataStore; diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index 235851630..25e246770 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -32,6 +32,28 @@ GLResourceManager::GLResourceManager(CaptureState &state, WrappedOpenGL *driver) { } +void GLResourceManager::UnregisterResource(GLResource res) +{ + auto it = m_Resources.find(res); + if(it != m_Resources.end()) + { + ResourceId id = it->second.first; + m_Names.erase(id); + + ReleaseResource(id); + m_Resources.erase(res); + + auto fboit = m_FBOAttachmentsCache.find(id); + if(fboit != m_FBOAttachmentsCache.end()) + { + delete fboit->second; + m_FBOAttachmentsCache.erase(fboit); + } + + m_Driver->RemoveAnnotations(id); + } +} + bool GLResourceManager::IsResourceTrackedForPersistency(const GLResource &res) { return res.Namespace == eResTexture || res.Namespace == eResBuffer; diff --git a/renderdoc/driver/gl/gl_manager.h b/renderdoc/driver/gl/gl_manager.h index a0bc5496b..31fd1378e 100644 --- a/renderdoc/driver/gl/gl_manager.h +++ b/renderdoc/driver/gl/gl_manager.h @@ -165,25 +165,7 @@ public: return false; } - void UnregisterResource(GLResource res) - { - auto it = m_Resources.find(res); - if(it != m_Resources.end()) - { - ResourceId id = it->second.first; - m_Names.erase(id); - - ReleaseResource(id); - m_Resources.erase(res); - - auto fboit = m_FBOAttachmentsCache.find(id); - if(fboit != m_FBOAttachmentsCache.end()) - { - delete fboit->second; - m_FBOAttachmentsCache.erase(fboit); - } - } - } + void UnregisterResource(GLResource res); ResourceId GetResID(GLResource res) { diff --git a/renderdoc/driver/gl/gl_stringise.cpp b/renderdoc/driver/gl/gl_stringise.cpp index b823903f5..45aa623f0 100644 --- a/renderdoc/driver/gl/gl_stringise.cpp +++ b/renderdoc/driver/gl/gl_stringise.cpp @@ -54,6 +54,8 @@ rdcstr DoStringise(const GLChunk &el) STRINGISE_ENUM_CLASS(eglSwapBuffersWithDamageEXT); STRINGISE_ENUM_CLASS(eglSwapBuffersWithDamageKHR); + STRINGISE_ENUM_CLASS_NAMED(SetCommandAnnotation, "Internal::SetCommandAnnotation"); + STRINGISE_ENUM_CLASS_NAMED(ImplicitThreadSwitch, "Internal::Implicit thread context-switch"); // re-use list of GL functions as chunks. Many of these will be aliased. This may not appear in the diff --git a/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp index ff8168139..38de8c37c 100644 --- a/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp @@ -235,6 +235,131 @@ void WrappedOpenGL::glObjectPtrLabel(const void *ptr, GLsizei length, const GLch } } +template +bool WrappedOpenGL::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); + } + + GetReplay()->WriteFrameRecord().frameInfo.containsAnnotations = true; + } + } + + return true; +} + +uint32_t WrappedOpenGL::SetCommandAnnotation(void *queueOrCommandBuffer, const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + if(queueOrCommandBuffer != NULL) + return 2; + + if(IsActiveCapturing(m_State)) + { + SERIALISE_TIME_CALL(); + + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + GET_SERIALISER.SetActionChunk(); + SCOPED_SERIALISE_CHUNK(GLChunk::SetCommandAnnotation); + + RENDERDOC_AnnotationValue val = value ? *value : RENDERDOC_AnnotationValue(); + + if(valueType == eRENDERDOC_APIObject && val.apiObject) + { + RENDERDOC_GLResourceReference *reference = (RENDERDOC_GLResourceReference *)val.apiObject; + ResourceId id = GetResourceManager()->GetResID( + GetResource((GLenum)reference->identifier, reference->name)); + 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; + } + + return 0; +} + +uint32_t WrappedOpenGL::SetObjectAnnotation(void *object, const char *key, + RENDERDOC_AnnotationType valueType, + uint32_t valueVectorWidth, + const RENDERDOC_AnnotationValue *value) +{ + RENDERDOC_GLResourceReference *reference = (RENDERDOC_GLResourceReference *)object; + ResourceId id = + GetResourceManager()->GetResID(GetResource((GLenum)reference->identifier, reference->name)); + + if(id != ResourceId()) + { + RENDERDOC_AnnotationValue val = value ? *value : RENDERDOC_AnnotationValue(); + + // Convert API object references to ResourceId + if(valueType == eRENDERDOC_APIObject && val.apiObject) + { + reference = (RENDERDOC_GLResourceReference *)val.apiObject; + ResourceId valId = + GetResourceManager()->GetResID(GetResource((GLenum)reference->identifier, reference->name)); + 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 WrappedOpenGL::glDebugMessageCallback(GLDEBUGPROC callback, const void *userParam) { GetCtxData().m_RealDebugFunc = callback; @@ -600,3 +725,6 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glInsertEventMarkerEXT, GLsizei length, co INSTANTIATE_FUNCTION_SERIALISED(void, glPushDebugGroup, GLenum source, GLuint id, GLsizei length, const GLchar *message); INSTANTIATE_FUNCTION_SERIALISED(void, glPopDebugGroup); +INSTANTIATE_FUNCTION_SERIALISED(void, SetCommandAnnotation, rdcstr key, + RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth, + RENDERDOC_AnnotationValue value);