diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 288d704b1..58a9849ad 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -1624,7 +1624,7 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, callback(start, end, us); } -QVariant SDObject2Variant(const SDObject *obj) +QVariant SDObject2Variant(const SDObject *obj, bool inlineImportant) { QVariant param; @@ -1649,9 +1649,100 @@ QVariant SDObject2Variant(const SDObject *obj) { switch(obj->type.basetype) { - case SDBasic::Chunk: param = QVariant(); break; - case SDBasic::Struct: param = QFormatStr("%1()").arg(obj->type.name); break; - case SDBasic::Array: param = QFormatStr("%1[]").arg(obj->type.name); break; + case SDBasic::Chunk: + { + if(inlineImportant) + { + QString name = obj->name; + + // don't display any "ClassName::" prefix by default here + int nsSep = name.indexOf(lit("::")); + if(nsSep > 0) + name.remove(0, nsSep + 2); + + name += lit("("); + + bool onlyImportant(obj->type.flags & SDTypeFlags::ImportantChildren); + + bool first = true; + for(const SDObject *child : *obj) + { + // never display hidden members + if(child->type.flags & SDTypeFlags::Hidden) + continue; + + if(!onlyImportant || (child->type.flags & SDTypeFlags::Important)) + { + if(!first) + name += lit(", "); + name += SDObject2Variant(child, true).toString(); + first = false; + } + } + + name += lit(")"); + param = name; + } + else + { + param = QVariant(); + } + break; + } + case SDBasic::Struct: + case SDBasic::Array: + { + // only inline important arrays with up to 4 elements + if(inlineImportant && (obj->type.basetype == SDBasic::Struct || obj->NumChildren() <= 4)) + { + int numImportantChildren = 0; + + if(obj->NumChildren() == 0) + { + param = lit("{}"); + } + else + { + bool importantChildren(obj->type.flags & SDTypeFlags::ImportantChildren); + + bool first = true; + QString s; + for(size_t i = 0; i < obj->NumChildren(); i++) + { + const SDObject *child = obj->GetChild(i); + + if(!importantChildren || (obj->GetChild(i)->type.flags & SDTypeFlags::Important)) + { + if(!first) + s += lit(", "); + first = false; + s += SDObject2Variant(child, true).toString(); + numImportantChildren++; + } + } + + // when a struct only has one important member, just display that as-if it were the + // struct. We rely on the underlying important flagging to not make things too + // confusing. + // This addresses case where there's a struct hierarchy but we only care about one + // member a level or two down - we don't end up with { { { { Resource } } } } + // it also helps with structs that we want to display as just a single thing - like a + // struct that references a resource with adjacent properties, which we want to elide to + // just the resource itself. + if(importantChildren && numImportantChildren == 1 && obj->type.basetype == SDBasic::Struct) + param = s; + else + param = QFormatStr("{ %1 }").arg(s); + } + } + else + { + param = obj->type.basetype == SDBasic::Array + ? QFormatStr("%1[%2]").arg(obj->type.name).arg(obj->NumChildren()) + : QFormatStr("%1()").arg(obj->type.name); + } + break; + } case SDBasic::Null: param = lit("NULL"); break; case SDBasic::Buffer: param = lit("(%1 bytes)").arg(obj->type.byteSize); break; case SDBasic::String: @@ -1694,7 +1785,7 @@ void addStructuredChildren(RDTreeWidgetItem *parent, const SDObject &parentObj) RDTreeWidgetItem *item = new RDTreeWidgetItem({name, QString()}); - item->setText(1, SDObject2Variant(obj)); + item->setText(1, SDObject2Variant(obj, false)); if(obj->type.basetype == SDBasic::Chunk || obj->type.basetype == SDBasic::Struct || obj->type.basetype == SDBasic::Array) @@ -3333,7 +3424,12 @@ QVariant StructuredDataItemModel::data(const QModelIndex &index, int role) const return QFormatStr("[%1]").arg(index.row()); else return obj->name; - case Value: return SDObject2Variant(obj); + case Value: + { + QVariant v = SDObject2Variant(obj, obj->GetParent() ? false : true); + RichResourceTextInitialise(v, NULL); + return v; + } case Type: return obj->type.name; } } diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 7c5289b54..845d8ef79 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -126,7 +126,7 @@ void CombineUsageEvents( class RDTreeWidgetItem; -QVariant SDObject2Variant(const SDObject *obj); +QVariant SDObject2Variant(const SDObject *obj, bool inlineImportant); void addStructuredChildren(RDTreeWidgetItem *parent, const SDObject &parentObj); struct PointerTypeRegistry diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp index f1acbbe3a..89a5bf241 100644 --- a/qrenderdoc/Windows/APIInspector.cpp +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -253,7 +253,7 @@ void APIInspector::addEvent(const APIEvent &ev, bool primary) m_Chunks.push_back(chunk); - root->setText(1, chunk->name); + root->setText(1, SDObject2Variant(chunk, true)); addStructuredChildren(root, *chunk); } diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index e25cdf346..dcb4aca4e 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -1201,7 +1201,8 @@ private: { for(const SDObject *c : *o) { - if(RichResourceTextFormat(*ctx, SDObject2Variant(c)).contains(paramValue, Qt::CaseInsensitive)) + if(RichResourceTextFormat(*ctx, SDObject2Variant(c, false)) + .contains(paramValue, Qt::CaseInsensitive)) return true; } @@ -1209,7 +1210,7 @@ private: } else { - return RichResourceTextFormat(*ctx, SDObject2Variant(o)) + return RichResourceTextFormat(*ctx, SDObject2Variant(o, false)) .contains(paramValue, Qt::CaseInsensitive); } diff --git a/renderdoc/api/replay/structured_data.h b/renderdoc/api/replay/structured_data.h index 25af4b446..1ab2d5a0c 100644 --- a/renderdoc/api/replay/structured_data.h +++ b/renderdoc/api/replay/structured_data.h @@ -145,6 +145,20 @@ DOCUMENT(R"(Bitfield flags that could be applied to a type. Special flag to indicate that this is structure is stored as a union, meaning all children share the same memory and some external flag indicates which element is valid. + +.. data:: Important + + Indicates that this object is important or significant, to aid in generating a summary/one-line + view of a particular chunk by only including important children. + + This property can be recursive - so an important child which is a structure can have only some + members which are important. + +.. data:: ImportantChildren + + Indicates that only important children should be processed, as noted in :data:`Important`. This + may appear on an object which has no important children - which indicates explicitly that there + are no important children so when summarising no parameters should be shown. )"); enum class SDTypeFlags : uint32_t { @@ -155,6 +169,8 @@ enum class SDTypeFlags : uint32_t NullString = 0x8, FixedArray = 0x10, Union = 0x20, + Important = 0x40, + ImportantChildren = 0x80, }; BITMASK_OPERATORS(SDTypeFlags); diff --git a/renderdoc/serialise/serialiser.h b/renderdoc/serialise/serialiser.h index 9393876ba..cbf18448f 100644 --- a/renderdoc/serialise/serialiser.h +++ b/renderdoc/serialise/serialiser.h @@ -1171,6 +1171,38 @@ public: return *this; } + Serialiser &Important() + { + if(ExportStructure() && !m_StructureStack.empty()) + { + SDObject ¤t = *m_StructureStack.back(); + + current.type.flags |= SDTypeFlags::ImportantChildren; + + if(current.NumChildren() > 0) + { + SDObject *last = current.GetChild(current.NumChildren() - 1); + last->type.flags |= SDTypeFlags::Important; + } + } + + return *this; + } + + Serialiser &Unimportant() + { + if(ExportStructure() && !m_StructureStack.empty()) + { + SDObject ¤t = *m_StructureStack.back(); + + // similar to Important() above but we *don't* set the important flag, we just mark the + // parent has + current.type.flags |= SDTypeFlags::ImportantChildren; + } + + return *this; + } + Serialiser &Named(const rdcstr &name) { if(ExportStructure() && !m_StructureStack.empty())