Add helpers for managing annotations in structured data

This commit is contained in:
baldurk committed 2026-01-28 14:42:09 +00:00
1 parent 8f0a408a2b
commit 650458beae
5 files changed
+329 -11

No files matched your search

+20
View File
@@ -669,6 +669,13 @@ typically it is one parent to many derived.
)");
rdcarray<ResourceId> parentResources;
DOCUMENT(R"(An optional set of annotations associated with this resource, may be ``None`` if
annotations are not used.
:type: SDObject
)");
SDObject *annotations = NULL;
DOCUMENT(R"(Utility function for setting up a custom name to overwrite the auto-generated one.
:param str givenName: The custom name to use.
@@ -964,6 +971,13 @@ markers added to the capture after load.
)");
uint64_t fileOffset = 0;
DOCUMENT(R"(An optional set of annotations associated with this event, may be ``None`` if
annotations are not used.
:type: SDObject
)");
SDObject *annotations = NULL;
static const uint32_t NoChunk = ~0U;
};
@@ -1855,6 +1869,12 @@ this counts the frame number when the capture was made.
)");
rdcarray<DebugMessage> debugMessages;
DOCUMENT(R"(Whether or not the capture contains any annotations.
:type: bool
)");
bool containsAnnotations = false;
static const uint32_t NoFrameNumber = ~0U;
};
+126 -7
View File
@@ -619,9 +619,9 @@ struct SDObject
PopulateAllChildren();
}
ret->data.children.resize(data.children.size());
ret->data.children.reserve(data.children.size());
for(size_t i = 0; i < data.children.size(); i++)
ret->data.children[i] = data.children[i]->Duplicate();
ret->AddAndOwnChild(data.children[i]->Duplicate());
return ret;
}
@@ -691,8 +691,7 @@ recursively through children.
// fully owned children. This shouldn't happen, but just in case we'll evaluate the lazy array
// here.
PopulateAllChildren();
data.children.push_back(child->Duplicate());
data.children.back()->m_Parent = this;
AddAndOwnChild(child->Duplicate());
}
DOCUMENT(R"(Find a child object by a given name. If no matching child is found, ``None`` is
returned.
@@ -734,6 +733,127 @@ The order of the search is not guaranteed, so care should be taken when the name
return NULL;
}
DOCUMENT(R"(Create a child object by a key path. Children will be created as necessary to ensure
that the key path exists, but if they already exist then the existing object will be returned.
Key paths are dotted recursive references, e.g. ``foo.bar`` is the member ``bar`` in parent ``foo``.
Arrays are represented by simple numeric entries e.g. ``foo.0.bar``, ``foo.1.bar``.
Empty entries are ignored so ``foo..bar`` and ``foo.bar`` refer to the same object.
Key paths must not be empty and must not begin with a dot.
If any path element is not unique in any child the behaviour is undefined, so objects should only be
manipulated by key path exclusively or not at all.
:param str keyPath: The key path to search for and return.
:return: A reference to the object at the relative key path
:rtype: SDObject
)");
inline SDObject *CreateChildByKeyPath(const rdcstr &keyPath)
{
if(keyPath.empty() || keyPath[0] == '.')
return this;
int dotIdx = keyPath.indexOf('.');
rdcstr element = keyPath.substr(0, dotIdx);
SDObject *child = NULL;
for(size_t i = 0; i < data.children.size(); i++)
if(GetChild(i)->name == element)
child = GetChild(i);
if(!child)
child = AddAndOwnChild(new SDObject(element, ""_lit));
while(dotIdx >= 0 && keyPath[dotIdx] == '.')
dotIdx++;
if(dotIdx >= 0 && dotIdx < keyPath.count())
return child->CreateChildByKeyPath(keyPath.substr(dotIdx));
return child;
}
DOCUMENT(R"(Find if a child object if it exists by a key path. Children will not be created
if any element of the path doesn't exist and instead ``None`` will be returned.
Key paths are dotted recursive references, e.g. ``foo.bar`` is the member ``bar`` in parent ``foo``.
Arrays are represented by simple numeric entries e.g. ``foo.0.bar``, ``foo.1.bar``.
Empty entries are ignored so ``foo..bar`` and ``foo.bar`` refer to the same object.
Key paths must not be empty and must not begin with a dot.
If any path element is not unique in any child the behaviour is undefined, so objects should only be
manipulated by key path exclusively or not at all.
:param str keyPath: The key path to search for and return.
:return: Whether or not a child exists at the given key path
:rtype: SDObject
)");
inline const SDObject *FindChildByKeyPath(const rdcstr &keyPath) const
{
if(keyPath.empty() || keyPath[0] == '.')
return this;
int dotIdx = keyPath.indexOf('.');
rdcstr element = keyPath.substr(0, dotIdx);
const SDObject *child = NULL;
for(size_t i = 0; i < data.children.size(); i++)
if(GetChild(i)->name == element)
child = GetChild(i);
if(!child)
return NULL;
while(dotIdx >= 0 && keyPath[dotIdx] == '.')
dotIdx++;
if(dotIdx >= 0 && dotIdx < keyPath.count())
return child->FindChildByKeyPath(keyPath.substr(dotIdx));
return child;
}
DOCUMENT(R"(Delete a child object by a key path. All children of the resulting path will be deleted
as well. If no such path exists, nothing will be changed.
If any path element is not unique in any child the behaviour is undefined, so objects should only be
manipulated by key path exclusively or not at all.
:param str keyPath: The key path to search for and delete.
)");
inline void EraseChildByKeyPath(const rdcstr &keyPath)
{
// shouldn't happen since we delete the found child (if it exists) from the parent, but return to be fault-tolerant
if(keyPath.empty() || keyPath[0] == '.')
return;
int dotIdx = keyPath.indexOf('.');
rdcstr element = keyPath.substr(0, dotIdx);
int childIdx = -1;
for(int i = 0; i < data.children.count(); i++)
if(GetChild(i)->name == element)
childIdx = i;
// if the child doesn't exist we can stop now
if(childIdx < 0)
return;
while(dotIdx >= 0 && keyPath[dotIdx] == '.')
dotIdx++;
if(dotIdx >= 0 && dotIdx < keyPath.count())
return GetChild(childIdx)->EraseChildByKeyPath(keyPath.substr(dotIdx));
RemoveChild(childIdx);
}
DOCUMENT(R"(Find a child object by a given index. If the index is out of bounds, ``None`` is
returned.
@@ -1500,12 +1620,11 @@ struct SDChunk : public SDObject
ret->data.basic = data.basic;
ret->data.str = data.str;
ret->data.children.resize(data.children.size());
PopulateAllChildren();
ret->data.children.reserve(data.children.size());
for(size_t i = 0; i < data.children.size(); i++)
ret->data.children[i] = data.children[i]->Duplicate();
ret->AddAndOwnChild(data.children[i]->Duplicate());
return ret;
}
+173
View File
@@ -68,6 +68,179 @@ RDOC_CONFIG(rdcarray<rdcstr>, Replay_Shader_LimitedSearchDirPaths, {},
"Companion array to DXBC.Debug.SearchDirPaths - listing paths which should not be "
"searched exhaustively but only used for simple lookups.");
void WriteAnnotation(SDObject *obj, RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth,
RENDERDOC_AnnotationValue value)
{
if(valueType == eRENDERDOC_Empty)
{
RDCERR("Invalid type of annotation to write");
return;
}
const rdcinflexiblestr types[eRENDERDOC_APIObject + 1][4] = {
// eRENDERDOC_Empty,
{""_lit, ""_lit, ""_lit, ""_lit},
// eRENDERDOC_Bool,
{"bool"_lit, "bool2"_lit, "bool3"_lit, "bool4"_lit},
// eRENDERDOC_Int32,
{"int"_lit, "int2"_lit, "int3"_lit, "int4"_lit},
// eRENDERDOC_UInt32,
{"uint"_lit, "uint2"_lit, "uint3"_lit, "uint4"_lit},
// eRENDERDOC_Int64,
{"long"_lit, "long2"_lit, "long3"_lit, "long4"_lit},
// eRENDERDOC_UInt64,
{"ulong"_lit, "ulong2"_lit, "ulong3"_lit, "ulong4"_lit},
// eRENDERDOC_Float,
{"float"_lit, "float2"_lit, "float3"_lit, "float4"_lit},
// eRENDERDOC_Double,
{"double"_lit, "double2"_lit, "double3"_lit, "double4"_lit},
// eRENDERDOC_String,
{"string"_lit, ""_lit, ""_lit, ""_lit},
// eRENDERDOC_APIObject,
{"ResourceId"_lit, ""_lit, ""_lit, ""_lit},
};
const uint32_t byteSize[eRENDERDOC_APIObject + 1] = {
// eRENDERDOC_Empty,
0,
// eRENDERDOC_Bool,
1,
// eRENDERDOC_Int32,
4,
// eRENDERDOC_UInt32,
4,
// eRENDERDOC_Int64,
8,
// eRENDERDOC_UInt64,
8,
// eRENDERDOC_Float,
4,
// eRENDERDOC_Double,
8,
// eRENDERDOC_String,
0,
// eRENDERDOC_APIObject,
8,
};
const SDBasic basetype[eRENDERDOC_APIObject + 1] = {
// eRENDERDOC_Empty,
SDBasic::Null,
// eRENDERDOC_Bool,
SDBasic::Boolean,
// eRENDERDOC_Int32,
SDBasic::SignedInteger,
// eRENDERDOC_UInt32,
SDBasic::UnsignedInteger,
// eRENDERDOC_Int64,
SDBasic::SignedInteger,
// eRENDERDOC_UInt64,
SDBasic::UnsignedInteger,
// eRENDERDOC_Float,
SDBasic::Float,
// eRENDERDOC_Double,
SDBasic::Float,
// eRENDERDOC_String,
SDBasic::String,
// eRENDERDOC_APIObject,
SDBasic::Resource,
};
if(valueVectorWidth > 1)
{
if(valueType == eRENDERDOC_APIObject)
{
RDCERR("Invalid vector width for API object");
return;
}
else if(valueType == eRENDERDOC_String)
{
RDCERR("Invalid vector width for string");
return;
}
const rdcinflexiblestr comps[] = {"x"_lit, "y"_lit, "z"_lit, "w"_lit};
obj->type.basetype = SDBasic::Struct;
obj->type.name = types[valueType][valueVectorWidth - 1];
RENDERDOC_AnnotationValue tmp = {};
for(uint32_t i = 0; i < valueVectorWidth; i++)
{
SDObject *child = obj->CreateChildByKeyPath(comps[i]);
if(byteSize[valueType] == 1)
memcpy(&tmp.boolean, &value.vector.boolean[i], sizeof(tmp.boolean));
if(byteSize[valueType] == 4)
memcpy(&tmp.uint32, &value.vector.uint32[i], sizeof(tmp.uint32));
if(byteSize[valueType] == 8)
memcpy(&tmp.uint64, &value.vector.uint64[i], sizeof(tmp.uint64));
WriteAnnotation(child, valueType, 0, tmp);
if(i == 0)
obj->type.byteSize = child->type.byteSize * valueVectorWidth;
}
return;
}
obj->type.name = types[valueType][0];
obj->type.basetype = basetype[valueType];
obj->type.byteSize = byteSize[valueType];
switch(valueType)
{
case eRENDERDOC_Empty:
case eRENDERDOC_AnnotationMax: RDCERR("Invalid annotation type"); return;
case eRENDERDOC_Bool:
{
obj->data.basic.b = value.boolean;
break;
}
case eRENDERDOC_Int32:
{
obj->data.basic.i = value.int32;
break;
}
case eRENDERDOC_UInt32:
{
obj->data.basic.u = value.uint32;
break;
}
case eRENDERDOC_Int64:
{
obj->data.basic.u = value.int64;
break;
}
case eRENDERDOC_UInt64:
{
obj->data.basic.u = value.uint64;
break;
}
case eRENDERDOC_Float:
{
obj->data.basic.d = value.float32;
break;
}
case eRENDERDOC_Double:
{
obj->data.basic.d = value.float64;
break;
}
case eRENDERDOC_String:
{
obj->type.byteSize = strlen(value.string);
obj->data.str = value.string;
break;
}
case eRENDERDOC_APIObject:
{
memcpy(&obj->data.basic.id, &value.uint64, sizeof(ResourceId));
break;
}
}
}
void LogReplayOptions(const ReplayOptions &opts)
{
RDCLOG("%s API validation during replay", (opts.apiValidation ? "Enabling" : "Not enabling"));
+3
View File
@@ -44,6 +44,9 @@ struct RDCThumb;
struct ReplayOptions;
struct SDObject;
void WriteAnnotation(SDObject *obj, RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth,
RENDERDOC_AnnotationValue value);
// not provided by tinyexr, just do by hand
bool is_exr_file(const byte *headerBuffer, size_t size);
void LogReplayOptions(const ReplayOptions &opts);
+7 -4
View File
@@ -442,8 +442,9 @@ void DoSerialise(SerialiserType &ser, ResourceDescription &el)
SERIALISE_MEMBER(initialisationChunks);
SERIALISE_MEMBER(derivedResources);
SERIALISE_MEMBER(parentResources);
// SERIALISE_MEMBER(annotations);
SIZE_CHECK(112);
SIZE_CHECK(120);
}
template <typename SerialiserType>
@@ -539,8 +540,9 @@ void DoSerialise(SerialiserType &ser, APIEvent &el)
SERIALISE_MEMBER(eventId);
SERIALISE_MEMBER(chunkIndex);
SERIALISE_MEMBER(fileOffset);
// SERIALISE_MEMBER(annotations);
SIZE_CHECK(16);
SIZE_CHECK(24);
}
template <typename SerialiserType>
@@ -773,8 +775,9 @@ void DoSerialise(SerialiserType &ser, FrameDescription &el)
SERIALISE_MEMBER(captureTime);
SERIALISE_MEMBER(stats);
SERIALISE_MEMBER(debugMessages);
SERIALISE_MEMBER(containsAnnotations);
SIZE_CHECK(504);
SIZE_CHECK(512);
}
template <typename SerialiserType>
@@ -783,7 +786,7 @@ void DoSerialise(SerialiserType &ser, FrameRecord &el)
SERIALISE_MEMBER(frameInfo);
SERIALISE_MEMBER(actionList);
SIZE_CHECK(528);
SIZE_CHECK(536);
}
template <typename SerialiserType>