From c2015de391e76a3626505a422240a82fc2964ec2 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 28 Nov 2025 15:11:15 +1300 Subject: [PATCH] Resource Usage UI changes Add a column that shows the parent marker for the event range (by default the end event ID) Add an option to prevent combining resource usage across markers (group by marker) The tooltip for a resource usage entry contains the full marker path --- qrenderdoc/Code/Interface/PersistantConfig.h | 12 ++- qrenderdoc/Code/QRDUtils.cpp | 91 +++++++++++++++++- qrenderdoc/Code/QRDUtils.h | 5 +- .../PipelineState/PipelineStateViewer.cpp | 2 +- qrenderdoc/Windows/ResourceInspector.cpp | 96 +++++++++++++++---- qrenderdoc/Windows/ResourceInspector.h | 3 + qrenderdoc/Windows/TextureViewer.cpp | 2 +- 7 files changed, 187 insertions(+), 24 deletions(-) diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index 54d52a22e..488b15888 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -662,7 +662,17 @@ DECLARE_REFLECTION_STRUCT(BugReport); CONFIG_SETTING(public, QVariantList, rdcarray, AlwaysLoad_Extensions) \ \ DOCUMENT(""); \ - CONFIG_SETTING(private, QVariantList, rdcarray, RemoteHostList) + CONFIG_SETTING(private, QVariantList, rdcarray, RemoteHostList) \ + \ + DOCUMENT(""); \ + DOCUMENT( \ + "``False`` if :class:`ResourceUsage` should combine resource usage across marker " \ + "boundaries.\n" \ + "\n:" \ + "Defaults to ``False``." \ + "" \ + ":type: bool"); \ + CONFIG_SETTING_VAL(public, bool, bool, ResourceUsage_SplitByMarker, false) DOCUMENT(R"(The formatting mode used when displaying fields marked as Offsets or Sizes. diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index be8865358..e5156dec0 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -1882,19 +1882,92 @@ float ConvertLinearToSRGB(float linear) return 1.055f * powf(linear, 1.0f / 2.4f) - 0.055f; } -void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, +static const ActionDescription *GetParentMarker(ICaptureContext &ctx, uint32_t eventId) +{ + const ActionDescription *parent = ctx.GetAction(eventId); + if(!parent) + { + rdcarray actions; + // Search the actions to find which action contains this eventId + for(const ActionDescription &action : ctx.CurRootActions()) + actions.push_back(&action); + + while(!parent && !actions.empty()) + { + const ActionDescription *action = actions.back(); + for(const APIEvent &event : action->events) + { + if(event.eventId == eventId) + { + parent = action; + break; + } + } + actions.pop_back(); + if(action->eventId < eventId) + { + for(const ActionDescription &child : action->children) + actions.push_back(&child); + } + } + } + while(parent != NULL && (parent->flags != ActionFlags::PushMarker)) + parent = parent->parent; + + return parent; +} + +QString GetParentMarkerName(ICaptureContext &ctx, uint32_t eventId) +{ + const ActionDescription *parent = GetParentMarker(ctx, eventId); + return parent ? QString(parent->customName) : QString(); +} + +QString GetParentMarkerPath(ICaptureContext &ctx, uint32_t eventId, bool &hasParent) +{ + const ActionDescription *parent = GetParentMarker(ctx, eventId); + + QString markerPath; + while(parent) + { + if(parent->flags & ActionFlags::PushMarker) + { + QString prevPath = markerPath; + markerPath = parent->customName; + if(!prevPath.isEmpty()) + { + markerPath += lit(" -> "); + markerPath += prevPath; + hasParent = true; + } + } + parent = parent->parent; + } + return markerPath; +} + +uint32_t GetParentMarkerEventId(ICaptureContext &ctx, uint32_t eventId) +{ + const ActionDescription *parent = GetParentMarker(ctx, eventId); + return parent ? parent->eventId : 0; +} + +void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, bool splitByMarker, std::function callback) { uint32_t start = 0; uint32_t end = 0; ResourceUsage us = ResourceUsage::IndexBuffer; + uint32_t parentEID = 0; for(const EventUsage &u : usage) { if(start == 0) { start = end = u.eventId; us = u.usage; + + parentEID = GetParentMarkerEventId(ctx, u.eventId); } if(u.usage == us && u.eventId == end) @@ -1904,9 +1977,12 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, bool distinct = false; + const uint32_t newParentEID = GetParentMarkerEventId(ctx, u.eventId); + // if the usage is different from the last, add a new entry, // or if the previous action link is broken. - if(u.usage != us || action == NULL || action->previous == 0) + if(u.usage != us || action == NULL || action->previous == 0 || + (splitByMarker && (parentEID != newParentEID))) { distinct = true; } @@ -1920,6 +1996,16 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, while(prev != NULL && prev->eventId > end) { + if(splitByMarker) + { + const uint32_t prevParentEID = GetParentMarkerEventId(ctx, prev->eventId); + if(parentEID != prevParentEID) + { + distinct = true; + break; + } + } + if(!(prev->flags & (ActionFlags::Dispatch | ActionFlags::MeshDispatch | ActionFlags::Drawcall | ActionFlags::CmdList))) { @@ -1947,6 +2033,7 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, { start = end = u.eventId; us = u.usage; + parentEID = newParentEID; } } diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index a9bf563d4..3294be0e0 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -271,8 +271,11 @@ QString GetComponentString(byte mask); QIcon MakeSwatchIcon(QWidget *parentWidget, QColor swatchColor); float ConvertLinearToSRGB(float linear); void CombineUsageEvents( - ICaptureContext &ctx, const rdcarray &usage, + ICaptureContext &ctx, const rdcarray &usage, bool splitByMarker, std::function callback); +uint32_t GetParentMarkerEventId(ICaptureContext &ctx, uint32_t eventId); +QString GetParentMarkerName(ICaptureContext &ctx, uint32_t eventId); +QString GetParentMarkerPath(ICaptureContext &ctx, uint32_t eventId, bool &hasParent); class RDTreeWidgetItem; diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index ad4a097b1..306644ddf 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -1190,7 +1190,7 @@ void PipelineStateViewer::ShowResourceContextMenu(RDTreeWidget *widget, const QP m_Ctx.GetResourceInspector()->Inspect(id); }); - CombineUsageEvents(m_Ctx, usage, + CombineUsageEvents(m_Ctx, usage, false, [this, &contextMenu](uint32_t start, uint32_t end, ResourceUsage use) { AddResourceUsageEntry(contextMenu, start, end, use); }); diff --git a/qrenderdoc/Windows/ResourceInspector.cpp b/qrenderdoc/Windows/ResourceInspector.cpp index 1dc8023bb..480d6e0be 100644 --- a/qrenderdoc/Windows/ResourceInspector.cpp +++ b/qrenderdoc/Windows/ResourceInspector.cpp @@ -138,6 +138,8 @@ bool ResourceSorterModel::lessThan(const QModelIndex &source_left, const QModelI ResourceInspector::ResourceInspector(ICaptureContext &ctx, QWidget *parent) : QFrame(parent), ui(new Ui::ResourceInspector), m_Ctx(ctx) { + m_SplitByMarker = ctx.Config().ResourceUsage_SplitByMarker; + ui->setupUi(this); SetResourceNameDisplay(tr("No Resource Selected")); @@ -178,6 +180,10 @@ ResourceInspector::ResourceInspector(ICaptureContext &ctx, QWidget *parent) ui->relatedResources->setFont(Formatter::PreferredFont()); ui->resourceUsage->setFont(Formatter::PreferredFont()); + ui->resourceUsage->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(ui->resourceUsage, &QWidget::customContextMenuRequested, this, + &ResourceInspector::resourceUsage_contextMenu); + { RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this); ui->relatedResources->setHeader(header); @@ -190,8 +196,8 @@ ResourceInspector::ResourceInspector(ICaptureContext &ctx, QWidget *parent) RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this); ui->resourceUsage->setHeader(header); - ui->resourceUsage->setColumns({tr("EID"), tr("Usage")}); - header->setColumnStretchHints({-1, 1}); + ui->resourceUsage->setColumns({tr("EID"), tr("Usage"), tr("End Marker")}); + header->setColumnStretchHints({-1, -1, 1}); } QObject::connect(ui->resourceList, &QListView::activated, this, @@ -309,31 +315,47 @@ void ResourceInspector::Inspect(ResourceId id) ui->resourceUsage->setEnabled(false); ui->resourceUsage->addTopLevelItem(new RDTreeWidgetItem( - {QString(), tr("Resource usage not tracked for this type of resource")})); + {QString(), QString(), tr("Resource usage not tracked for this type of resource")})); } else if(usage.empty()) { - ui->resourceUsage->addTopLevelItem( - new RDTreeWidgetItem({QString(), tr("No static usage observed for this resource")})); + ui->resourceUsage->addTopLevelItem(new RDTreeWidgetItem( + {QString(), QString(), tr("No static usage observed for this resource")})); } else { - CombineUsageEvents( - m_Ctx, usage, [this](uint32_t startEID, uint32_t endEID, ResourceUsage use) { - QString text; + CombineUsageEvents(m_Ctx, usage, m_SplitByMarker, [this](uint32_t startEID, uint32_t endEID, ResourceUsage use) { + QString text; - if(startEID == endEID) - text = QFormatStr("EID %1").arg(startEID); - else - text = QFormatStr("EID %1-%2").arg(startEID).arg(endEID); + if(startEID == endEID) + text = QFormatStr("EID %1").arg(startEID); + else + text = QFormatStr("EID %1-%2").arg(startEID).arg(endEID); + uint32_t eid = m_SplitByMarker ? startEID : endEID; - RDTreeWidgetItem *item = - new RDTreeWidgetItem({text, ToQStr(use, m_Ctx.APIProps().pipelineType)}); - item->setData(0, ResourceIdRole, QVariant(endEID)); - item->setData(1, ResourceIdRole, QVariant(endEID)); + QString markerName(GetParentMarkerName(m_Ctx, eid)); + bool hasParent = false; + QString fullMarkerPath = GetParentMarkerPath(m_Ctx, eid, hasParent); - ui->resourceUsage->addTopLevelItem(item); - }); + RDTreeWidgetItem *item = + new RDTreeWidgetItem({text, ToQStr(use, m_Ctx.APIProps().pipelineType), markerName}); + item->setData(0, ResourceIdRole, QVariant(endEID)); + item->setData(1, ResourceIdRole, QVariant(endEID)); + item->setData(2, ResourceIdRole, QVariant(endEID)); + item->setToolTip(fullMarkerPath); + + if(hasParent) + { + RDTreeWidgetItem *child = new RDTreeWidgetItem({QString(), QString(), fullMarkerPath}); + child->setData(0, ResourceIdRole, QVariant(endEID)); + child->setData(1, ResourceIdRole, QVariant(endEID)); + child->setData(2, ResourceIdRole, QVariant(endEID)); + child->setToolTip(fullMarkerPath); + + item->addChild(child); + } + ui->resourceUsage->addTopLevelItem(item); + }); } ui->resourceUsage->endUpdate(); @@ -654,6 +676,44 @@ void ResourceInspector::on_resourceUsage_doubleClicked(const QModelIndex &index) m_Ctx.SetEventID({}, eid, eid); } +void ResourceInspector::on_resourceUsage_SplitByMarker_toggled() +{ + m_SplitByMarker = !m_SplitByMarker; + m_Ctx.Config().ResourceUsage_SplitByMarker = m_SplitByMarker; + m_Ctx.Config().Save(); + + // force a refresh to pick up the new grouping of usage + ResourceId id = m_Resource; + m_Resource = ResourceId(); + Inspect(id); + if(m_SplitByMarker) + ui->resourceUsage->setColumns({tr("EID"), tr("Usage"), tr("Start Marker")}); + else + ui->resourceUsage->setColumns({tr("EID"), tr("Usage"), tr("End Marker")}); +} + +void ResourceInspector::resourceUsage_contextMenu(const QPoint &pos) +{ + QMenu contextMenu(this); + QAction copyText(tr("Copy"), this); + QAction splitByMarker(tr("Split By Marker"), this); + splitByMarker.setCheckable(true); + splitByMarker.setChecked(m_SplitByMarker); + + RDTreeWidget *resourceUsage = ui->resourceUsage; + QModelIndex index = resourceUsage->indexAt(pos); + + QObject::connect(©Text, &QAction::triggered, + [resourceUsage, pos, index] { resourceUsage->copyIndex(pos, index); }); + QObject::connect(&splitByMarker, &QAction::triggered, + [this] { this->on_resourceUsage_SplitByMarker_toggled(); }); + + contextMenu.addAction(©Text); + contextMenu.addAction(&splitByMarker); + + RDDialog::show(&contextMenu, ui->resourceUsage->viewport()->mapToGlobal(pos)); +} + void ResourceInspector::enterEvent(QEvent *event) { HighlightUsage(); diff --git a/qrenderdoc/Windows/ResourceInspector.h b/qrenderdoc/Windows/ResourceInspector.h index 50cffe9eb..5a3e60d41 100644 --- a/qrenderdoc/Windows/ResourceInspector.h +++ b/qrenderdoc/Windows/ResourceInspector.h @@ -103,10 +103,12 @@ public slots: // manual slots void resource_doubleClicked(const QModelIndex &index); + void resourceUsage_contextMenu(const QPoint &pos); private slots: void on_viewContents_clicked(); void on_resourceUsage_doubleClicked(const QModelIndex &index); + void on_resourceUsage_SplitByMarker_toggled(); protected: void enterEvent(QEvent *event) override; @@ -127,4 +129,5 @@ private: ResourceSorterModel *m_FilterModel; StructuredDataItemModel *m_ChunksModel; RichTextViewDelegate *m_delegate; + bool m_SplitByMarker = false; }; diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 7f2fb63b1..a633f171d 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -2392,7 +2392,7 @@ void TextureViewer::OpenResourceContextMenu(ResourceId id, bool input, m_Ctx.GetResourceInspector()->Inspect(id); }); - CombineUsageEvents(m_Ctx, usage, + CombineUsageEvents(m_Ctx, usage, false, [this, &contextMenu](uint32_t start, uint32_t end, ResourceUsage use) { AddResourceUsageEntry(contextMenu, start, end, use); });