Add GL support for annotations

This commit is contained in:
baldurk
2026-01-22 11:13:12 +00:00
parent 727f450be2
commit c26a5405e9
9 changed files with 260 additions and 31 deletions
+23 -1
View File
@@ -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;
+4
View File
@@ -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;
}
+2
View File
@@ -2249,6 +2249,8 @@ enum class GLChunk : uint32_t
glFlushMappedBufferRangeEXT,
glClipControlEXT,
SetCommandAnnotation,
Max,
};
+57 -2
View File
@@ -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))
+21 -9
View File
@@ -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<ResourceId, SDObject *> m_Annotations; // Object annotations by ResourceId
SDObject *m_RootAnnotation = NULL; // Root for event annotation state
rdcarray<SDObject *> m_EventAnnotations; // Track allocations for cleanup
GLContextTLSData m_EmptyTLSData;
uint64_t m_CurCtxDataTLS;
rdcarray<GLContextTLSData *> m_CtxDataVector;
@@ -574,6 +580,12 @@ private:
rdcarray<QueuedResource> m_QueuedInitialFetches;
rdcarray<QueuedResource> 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 <typename SerialiserType>
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<int, bytebuf> CompressedDataStore;
+22
View File
@@ -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;
+1 -19
View File
@@ -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)
{
+2
View File
@@ -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
@@ -235,6 +235,131 @@ void WrappedOpenGL::glObjectPtrLabel(const void *ptr, GLsizei length, const GLch
}
}
template <typename SerialiserType>
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);