diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 20801cb5f..3992cfed2 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -230,9 +230,6 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) ui->EventBrowser_ApplyColors->setChecked(m_Ctx.Config().EventBrowser_ApplyColors); ui->EventBrowser_ColorEventRow->setChecked(m_Ctx.Config().EventBrowser_ColorEventRow); - // disable sub-checkbox - ui->EventBrowser_ColorEventRow->setEnabled(ui->EventBrowser_ApplyColors->isChecked()); - ui->Comments_ShowOnLoad->setChecked(m_Ctx.Config().Comments_ShowOnLoad); ui->Formatter_MinFigures->setValue(m_Ctx.Config().Formatter_MinFigures); @@ -1030,6 +1027,9 @@ void SettingsDialog::on_EventBrowser_ApplyColors_toggled(bool checked) { m_Ctx.Config().EventBrowser_ApplyColors = ui->EventBrowser_ApplyColors->isChecked(); + // disable sub-checkbox + ui->EventBrowser_ColorEventRow->setEnabled(ui->EventBrowser_ApplyColors->isChecked()); + m_Ctx.Config().Save(); } diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index fca851217..ee074af4f 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -1100,7 +1100,7 @@ After interop is enabled you will need to reload any capture. - Apply marker colors (requires file reload) + Apply marker colors diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 7a82ac375..1dd2ae5e0 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "EventBrowser.h" +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include "Code/QRDUtils.h" @@ -40,21 +42,6 @@ #include "scintilla/include/qt/ScintillaEdit.h" #include "ui_EventBrowser.h" -struct EventItemTag -{ - EventItemTag() = default; - EventItemTag(uint32_t eventId) : EID(eventId), lastEID(eventId) {} - EventItemTag(uint32_t eventId, uint32_t lastEventID) : EID(eventId), lastEID(lastEventID) {} - uint32_t EID = 0; - uint32_t lastEID = 0; - double duration = -1.0; - bool current = false; - bool find = false; - bool bookmark = false; -}; - -Q_DECLARE_METATYPE(EventItemTag); - enum { COL_NAME, @@ -64,6 +51,616 @@ enum COL_COUNT, }; +enum +{ + ROLE_SELECTED_EID = Qt::UserRole, + ROLE_EFFECTIVE_EID, +}; + +static uint32_t GetSelectedEID(QModelIndex idx) +{ + return idx.data(ROLE_SELECTED_EID).toUInt(); +} + +static uint32_t GetEffectiveEID(QModelIndex idx) +{ + return idx.data(ROLE_EFFECTIVE_EID).toUInt(); +} + +struct EventItemModel : public QAbstractItemModel +{ + EventItemModel(QAbstractItemView *view, ICaptureContext &ctx) : m_View(view), m_Ctx(ctx) + { + UpdateDurationColumn(); + + m_CurrentEID = createIndex(0, 0, TagCaptureStart); + } + + void ResetModel() + { + emit beginResetModel(); + emit endResetModel(); + + m_Nodes.clear(); + + if(!m_Ctx.CurDrawcalls().empty()) + m_Nodes[0] = CreateDrawNode(NULL); + + m_CurrentEID = createIndex(0, 0, TagCaptureStart); + + RefreshCache(); + } + + void RefreshCache() + { + if(!m_Ctx.IsCaptureLoaded()) + return; + + uint32_t eid = m_Ctx.CurSelectedEvent(); + + m_MessageCounts[eid] = m_Ctx.CurPipelineState().GetShaderMessages().count(); + + if(eid != data(m_CurrentEID, ROLE_SELECTED_EID) || eid == 0) + { + QModelIndex oldCurrent = m_CurrentEID; + + m_CurrentEID = GetIndexForEID(eid); + + if(eid == 0 && m_Ctx.CurEvent() != 0) + m_CurrentEID = createIndex(0, 0, TagRoot); + + RefreshIcon(oldCurrent); + RefreshIcon(m_CurrentEID); + } + + if(m_Ctx.GetBookmarks() != m_Bookmarks) + { + rdcarray indices; + indices.swap(m_BookmarkIndices); + + m_Bookmarks = m_Ctx.GetBookmarks(); + + for(const EventBookmark &b : m_Bookmarks) + m_BookmarkIndices.push_back(GetIndexForEID(b.eventId)); + + for(QModelIndex idx : indices) + RefreshIcon(idx); + for(QModelIndex idx : m_BookmarkIndices) + RefreshIcon(idx); + } + } + + bool HasTimes() { return !m_Times.empty(); } + void SetTimes(const rdcarray ×) + { + // set all times for events to -1.0 + m_Times.fill(m_Nodes[0].effectiveEID + 1, -1.0); + + // fill in the actual times + for(const CounterResult &r : times) + { + if(r.eventId < m_Times.size()) + { + m_Times[r.eventId] = r.value.d; + } + } + + // iterate nodes in reverse order, because parent nodes will always be before children + // so we know we'll have the results + QList nodeEIDs = m_Nodes.keys(); + for(auto it = nodeEIDs.rbegin(); it != nodeEIDs.rend(); it++) + CalculateTotalDuration(m_Nodes[*it]); + + // Qt's item model kind of sucks and doesn't have a good way to say "all data in this column has + // changed" let alone "all data has changed". dataChanged() is limited to only a group of model + // indices under a single parent. Instead we just force the view itself to refresh here. + m_View->viewport()->update(); + } + + void UpdateDurationColumn() + { + m_TimeUnit = m_Ctx.Config().EventBrowser_TimeUnit; + emit headerDataChanged(Qt::Horizontal, COL_DURATION, COL_DURATION); + + m_View->viewport()->update(); + } + + void SetFindText(QString text) + { + if(m_FindString == text) + return; + + rdcarray oldResults; + oldResults.swap(m_FindResults); + + m_FindString = text; + m_FindResults.clear(); + + if(!m_FindString.isEmpty()) + { + // do a depth-first search to find results + AccumulateFindResults(createIndex(0, 0, TagRoot)); + } + + for(QModelIndex i : oldResults) + RefreshIcon(i); + for(QModelIndex i : m_FindResults) + RefreshIcon(i); + } + + QModelIndex Find(bool forward) + { + if(m_FindResults.empty()) + return QModelIndex(); + + // if we're already on a find result we can just zoom to the next one + int idx = m_FindResults.indexOf(m_CurrentEID); + if(idx >= 0) + { + if(forward) + idx++; + else + idx--; + if(idx < 0) + idx = m_FindResults.count() - 1; + + idx %= m_FindResults.count(); + + return m_FindResults[idx]; + } + + // otherwise we need to do a more expensive search. Get the EID for the current, and find the + // next find result after that (wrapping around) + uint32_t eid = data(m_CurrentEID, ROLE_EFFECTIVE_EID).toUInt(); + + if(forward) + { + // find the first result >= our selected EID + for(int i = 0; i < m_FindResults.count(); i++) + { + uint32_t findEID = data(m_FindResults[i], ROLE_SELECTED_EID).toUInt(); + if(findEID >= eid) + return m_FindResults[i]; + } + + // if we didn't find any, we're past all the results - return the first one to wrap + return m_FindResults[0]; + } + else + { + // find the last result <= our selected EID + for(int i = m_FindResults.count() - 1; i >= 0; i--) + { + uint32_t findEID = data(m_FindResults[i], ROLE_SELECTED_EID).toUInt(); + if(findEID <= eid) + return m_FindResults[i]; + } + + // if we didn't find any, we're before all the results - return the last one to wrap + return m_FindResults.back(); + } + } + + int NumFindResults() { return m_FindResults.count(); } + QModelIndex GetIndexForEID(uint32_t eid) + { + if(eid == 0) + return createIndex(0, 0, TagCaptureStart); + + const DrawcallDescription *draw = m_Ctx.GetDrawcall(eid); + if(draw) + { + int rowInParent; + if(draw->parent) + { + rowInParent = GetRowIndexForEIDInDraw(draw->parent->children, draw->eventId); + } + else + { + // add 1 to account for Capture Start + rowInParent = 1 + GetRowIndexForEIDInDraw(m_Ctx.CurDrawcalls(), draw->eventId); + } + + return createIndex(rowInParent, 0, (void *)draw); + } + else + { + qWarning() << "Couldn't find draw for event" << eid; + } + + return QModelIndex(); + } + + ////////////////////////////////////////////////////////////////////////////////////// + // + // QAbstractItemModel methods + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override + { + if(!m_Ctx.IsCaptureLoaded()) + return QModelIndex(); + + // create fake root if the parent is invalid + if(!parent.isValid()) + return createIndex(0, column, TagRoot); + + // if the parent is the root, return the index for the specified child if it's in bounds + if(parent.internalId() == TagRoot) + { + // first child is the capture start + if(row == 0) + return createIndex(row, column, TagCaptureStart); + + // the rest are in the root draws list + const rdcarray &draws = m_Ctx.CurDrawcalls(); + + if(row > 0 && row <= draws.count()) + return createIndex(row, column, (void *)&draws[row - 1]); + } + else if(parent.internalId() == TagCaptureStart) + { + // no children for this + return QModelIndex(); + } + else + { + const DrawcallDescription *parentDraw = (const DrawcallDescription *)parent.internalPointer(); + + // otherwise the parent is a real draw + if(row >= 0 && row < parentDraw->children.count()) + return createIndex(row, column, (void *)&parentDraw->children[row]); + } + + return QModelIndex(); + } + + QModelIndex parent(const QModelIndex &index) const override + { + if(!m_Ctx.IsCaptureLoaded() || !index.isValid() || index.internalId() == TagRoot) + return QModelIndex(); + + // Capture Start's parent is the root + if(index.internalId() == TagCaptureStart) + return createIndex(0, 0, TagRoot); + + // otherwise it's a draw + const DrawcallDescription *draw = (const DrawcallDescription *)index.internalPointer(); + + // if it has no parent draw, the parent is the root + if(draw->parent == NULL) + return createIndex(0, 0, TagRoot); + + // Qt needs the row of the child in the parent in the grand-parent, do so with lower_bound + const DrawcallDescription *parentDraw = draw->parent; + + int rowInParent; + if(parentDraw->parent) + { + rowInParent = GetRowIndexForEIDInDraw(parentDraw->parent->children, parentDraw->eventId); + } + else + { + // add 1 to account for Capture Start + rowInParent = 1 + GetRowIndexForEIDInDraw(m_Ctx.CurDrawcalls(), parentDraw->eventId); + } + + return createIndex(rowInParent, 0, (void *)parentDraw); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + if(!m_Ctx.IsCaptureLoaded()) + return 0; + + // only one root + if(!parent.isValid()) + return 1; + + // only column 1 has children (Qt convention) + if(parent.column() != 0) + return 0; + + // +1 for the capture start + if(parent.internalId() == TagRoot) + return m_Ctx.CurDrawcalls().count() + 1; + + if(parent.internalId() == TagCaptureStart) + return 0; + + // otherwise it's a draw + const DrawcallDescription *draw = (const DrawcallDescription *)parent.internalPointer(); + + return draw->children.count(); + } + + int columnCount(const QModelIndex &parent = QModelIndex()) const override { return COL_COUNT; } + QVariant headerData(int section, Qt::Orientation orientation, int role) const override + { + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) + { + case COL_NAME: return tr("Name"); + case COL_EID: return lit("EID"); + case COL_DRAW: return lit("Draw #"); + case COL_DURATION: return tr("Duration (%1)").arg(UnitSuffix(m_TimeUnit)); + default: break; + } + } + + return QVariant(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if(!index.isValid()) + return QVariant(); + + if(role == Qt::DecorationRole) + { + if(index == m_CurrentEID) + return Icons::flag_green(); + else if(m_BookmarkIndices.contains(index)) + return Icons::asterisk_orange(); + else if(m_FindResults.contains(index)) + return Icons::find(); + return QVariant(); + } + + if(index.column() == COL_DURATION && role == Qt::TextAlignmentRole) + return int(Qt::AlignRight | Qt::AlignVCenter); + + if(index.internalId() == TagRoot) + { + if(role == Qt::DisplayRole && index.column() == COL_NAME) + { + uint32_t frameNumber = m_Ctx.FrameInfo().frameNumber; + return frameNumber == ~0U ? tr("User-defined Capture") : tr("Frame #%1").arg(frameNumber); + } + + if(role == ROLE_SELECTED_EID) + return 0; + + if(role == ROLE_EFFECTIVE_EID) + return m_Nodes[0].effectiveEID; + + if(index.column() == COL_DURATION && role == Qt::DisplayRole) + return FormatDuration(0); + } + else if(index.internalId() == TagCaptureStart) + { + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case COL_NAME: return tr("Capture Start"); + case COL_EID: return lit("0"); + case COL_DRAW: return lit("0"); + default: break; + } + } + + if(role == ROLE_SELECTED_EID || role == ROLE_EFFECTIVE_EID) + return 0; + + if(index.column() == COL_DURATION && role == Qt::DisplayRole) + return FormatDuration(~0U); + } + else + { + const DrawcallDescription *draw = (const DrawcallDescription *)index.internalPointer(); + + if(role == ROLE_SELECTED_EID) + return draw->eventId; + + if(role == ROLE_EFFECTIVE_EID) + { + auto it = m_Nodes.find(draw->eventId); + if(it != m_Nodes.end()) + return it->effectiveEID; + return draw->eventId; + } + + if(index.column() == COL_DURATION && role == Qt::DisplayRole) + { + return FormatDuration(draw->eventId); + } + + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case COL_NAME: + { + QString name = draw->name; + + if(m_MessageCounts.contains(draw->eventId)) + { + int count = m_MessageCounts[draw->eventId]; + if(count > 0) + name += lit(" __rd_msgs::%1:%2").arg(draw->eventId).arg(count); + } + + QVariant v = QString(name); + RichResourceTextInitialise(v, &m_Ctx); + return v; + } + case COL_EID: return draw->eventId; + case COL_DRAW: return draw->drawcallId; + default: break; + } + } + else if(role == Qt::BackgroundRole || role == Qt::ForegroundRole || + role == RDTreeView::TreeLineColorRole) + { + if(m_Ctx.Config().EventBrowser_ApplyColors) + { + if(!m_Ctx.Config().EventBrowser_ColorEventRow && role != RDTreeView::TreeLineColorRole) + return QVariant(); + + // if alpha isn't 0, assume the colour is valid + if((draw->flags & (DrawFlags::PushMarker | DrawFlags::SetMarker)) && + draw->markerColor.w > 0.0f) + { + QColor col = + QColor::fromRgb(qRgb(draw->markerColor.x * 255.0f, draw->markerColor.y * 255.0f, + draw->markerColor.z * 255.0f)); + + if(role == Qt::BackgroundRole || role == RDTreeView::TreeLineColorRole) + return QBrush(col); + else if(role == Qt::ForegroundRole) + return QBrush(contrastingColor(col, m_View->palette().color(QPalette::Text))); + } + } + } + } + + return QVariant(); + } + +private: + ICaptureContext &m_Ctx; + + QAbstractItemView *m_View; + + static const quintptr TagRoot = 0x0; + static const quintptr TagCaptureStart = 0x1; + + rdcarray m_Times; + TimeUnit m_TimeUnit = TimeUnit::Count; + + QModelIndex m_CurrentEID; + rdcarray m_Bookmarks; + rdcarray m_BookmarkIndices; + QString m_FindString; + rdcarray m_FindResults; + + QMap m_MessageCounts; + + // we don't want to have something for every event/draw, so we cache only for each nested node. + // This drastically limits our worst case N - some hierarchies have more nodes than others but + // even in hierarchies with lots of nodes the number is small, compared to draws/events which + // could be 1000s to 100,000s even. + // we cache this with a depth-first search at init time + struct DrawTreeNode + { + const DrawcallDescription *draw; + uint32_t effectiveEID; + }; + QMap m_Nodes; + + void AccumulateFindResults(QModelIndex root) + { + for(int i = 0, count = rowCount(root); i < count; i++) + { + QModelIndex idx = index(i, 0, root); + + // check if there's a match first + QString name = data(idx).toString(); + + if(name.contains(m_FindString, Qt::CaseInsensitive)) + m_FindResults.push_back(idx); + + // now recurse + AccumulateFindResults(idx); + } + } + + void RefreshIcon(QModelIndex idx) + { + emit dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), COL_COUNT - 1), + {Qt::DecorationRole, Qt::DisplayRole}); + } + + QVariant FormatDuration(uint32_t eid) const + { + if(m_Times.empty()) + return lit("---"); + + double secs = eid < m_Times.size() ? m_Times[eid] : -1.0; + + if(secs < 0.0) + return QVariant(); + + if(m_TimeUnit == TimeUnit::Milliseconds) + secs *= 1000.0; + else if(m_TimeUnit == TimeUnit::Microseconds) + secs *= 1000000.0; + else if(m_TimeUnit == TimeUnit::Nanoseconds) + secs *= 1000000000.0; + + return Formatter::Format(secs); + } + + int GetRowIndexForEIDInDraw(const rdcarray &draws, uint32_t eid) const + { + DrawcallDescription search; + search.eventId = eid; + + const DrawcallDescription *f = + std::lower_bound(draws.begin(), draws.end(), search, + [](const DrawcallDescription &a, const DrawcallDescription &b) { + return a.eventId < b.eventId; + }); + + return f - draws.begin(); + } + + void CalculateTotalDuration(DrawTreeNode &node) + { + const rdcarray &drawChildren = + node.draw ? node.draw->children : m_Ctx.CurDrawcalls(); + + double duration = 0.0; + + for(const DrawcallDescription &d : drawChildren) + { + // ignore out of bounds EIDs - should not happen + if(d.eventId >= m_Times.size()) + continue; + + // add the time for this event, if it's non-negative. Because we fill out nodes in reverse + // order, any children that are nodes themselves should be populated by now + duration += qMax(0.0, m_Times[d.eventId]); + } + + m_Times[node.draw ? node.draw->eventId : 0] = duration; + } + + DrawTreeNode CreateDrawNode(const DrawcallDescription *draw) + { + const rdcarray &drawRange = draw ? draw->children : m_Ctx.CurDrawcalls(); + + DrawTreeNode ret; + + ret.draw = draw; + ret.effectiveEID = drawRange.back().eventId; + + for(int i = 0; i < drawRange.count(); i++) + { + const DrawcallDescription &d = drawRange[i]; + if(d.children.empty()) + continue; + + DrawTreeNode node = CreateDrawNode(&d); + + if(d.eventId == ret.effectiveEID) + ret.effectiveEID = node.effectiveEID; + + m_Nodes[d.eventId] = node; + } + + return ret; + } +}; + +struct EventFilterModel : public QSortFilterProxyModel +{ + EventFilterModel(ICaptureContext &ctx) : m_Ctx(ctx) {} +private: + ICaptureContext &m_Ctx; +}; + static bool textEditControl(QWidget *sender) { if(qobject_cast(sender) || qobject_cast(sender) || @@ -86,13 +683,19 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) clearBookmarks(); + m_Model = new EventItemModel(ui->events, m_Ctx); + m_FilterModel = new EventFilterModel(m_Ctx); + m_FilterModel->setSourceModel(m_Model); + + ui->events->setModel(m_FilterModel); + + m_delegate = new RichTextViewDelegate(ui->events); + ui->events->setItemDelegate(m_delegate); + ui->jumpToEID->setFont(Formatter::PreferredFont()); ui->find->setFont(Formatter::PreferredFont()); ui->events->setFont(Formatter::PreferredFont()); - ui->events->setColumns( - {tr("Name"), lit("EID"), lit("Draw #"), lit("Duration - replaced in UpdateDurationColumn")}); - ui->events->setHeader(new RDHeaderView(Qt::Horizontal, this)); ui->events->header()->setStretchLastSection(true); ui->events->header()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); @@ -103,8 +706,6 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) ui->events->header()->setSectionResizeMode(COL_DRAW, QHeaderView::Interactive); ui->events->header()->setSectionResizeMode(COL_DURATION, QHeaderView::Interactive); - ui->events->setColumnAlignment(COL_DURATION, Qt::AlignRight | Qt::AlignCenter); - ui->events->header()->setMinimumSectionSize(40); ui->events->header()->setSectionsMovable(true); @@ -136,7 +737,9 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) QObject::connect(ui->closeFind, &QToolButton::clicked, this, &EventBrowser::on_HideFindJump); QObject::connect(ui->closeJump, &QToolButton::clicked, this, &EventBrowser::on_HideFindJump); - QObject::connect(ui->events, &RDTreeWidget::keyPress, this, &EventBrowser::events_keyPress); + QObject::connect(ui->events, &RDTreeView::keyPress, this, &EventBrowser::events_keyPress); + QObject::connect(ui->events->selectionModel(), &QItemSelectionModel::currentChanged, this, + &EventBrowser::events_currentChanged); ui->jumpStrip->hide(); ui->findStrip->hide(); ui->bookmarkStrip->hide(); @@ -184,7 +787,7 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) [this](QWidget *) { on_HideFindJump(); }); ui->events->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ui->events, &RDTreeWidget::customContextMenuRequested, this, + QObject::connect(ui->events, &RDTreeView::customContextMenuRequested, this, &EventBrowser::events_contextMenu); ui->events->header()->setContextMenuPolicy(Qt::CustomContextMenu); @@ -242,25 +845,14 @@ void EventBrowser::OnCaptureLoaded() { ui->events->setIgnoreBackgroundColors(!m_Ctx.Config().EventBrowser_ColorEventRow); - uint32_t frameNumber = m_Ctx.FrameInfo().frameNumber; + // older Qt versions lose all the sections when a model resets even if the sections don't change. + // Manually save/restore them + QVariant p = persistData(); + m_Model->ResetModel(); + setPersistData(p); - QString rootName = - frameNumber == ~0U ? tr("User-defined Capture") : tr("Frame #%1").arg(frameNumber); - - RDTreeWidgetItem *frame = new RDTreeWidgetItem({rootName, QString(), QString(), QString()}); - - RDTreeWidgetItem *framestart = - new RDTreeWidgetItem({tr("Capture Start"), lit("0"), lit("0"), QString()}); - framestart->setTag(QVariant::fromValue(EventItemTag(0, 0))); - - frame->addChild(framestart); - - QPair lastEIDDraw = AddDrawcalls(frame, m_Ctx.CurDrawcalls()); - frame->setTag(QVariant::fromValue(EventItemTag(0, lastEIDDraw.first))); - - ui->events->addTopLevelItem(frame); - - ui->events->expandItem(frame); + // expand the root frame node + ui->events->expand(ui->events->model()->index(0, 0)); clearBookmarks(); repopulateBookmarks(); @@ -280,7 +872,11 @@ void EventBrowser::OnCaptureClosed() on_HideFindJump(); - ui->events->clear(); + // older Qt versions lose all the sections when a model resets even if the sections don't change. + // Manually save/restore them + QVariant p = persistData(); + m_Model->ResetModel(); + setPersistData(p); ui->find->setEnabled(false); ui->gotoEID->setEnabled(false); @@ -294,189 +890,10 @@ void EventBrowser::OnCaptureClosed() void EventBrowser::OnEventChanged(uint32_t eventId) { SelectEvent(eventId); - RefreshShaderMessages(); repopulateBookmarks(); highlightBookmarks(); -} -bool EventBrowser::ShouldHide(const DrawcallDescription &drawcall) -{ - if(drawcall.flags & DrawFlags::PushMarker) - { - if(m_Ctx.Config().EventBrowser_HideEmpty) - { - if(drawcall.children.isEmpty()) - return true; - - bool allhidden = true; - - for(const DrawcallDescription &child : drawcall.children) - { - if(ShouldHide(child)) - continue; - - allhidden = false; - break; - } - - if(allhidden) - return true; - } - - if(m_Ctx.Config().EventBrowser_HideAPICalls) - { - if(drawcall.children.isEmpty()) - return false; - - bool onlyapi = true; - - for(const DrawcallDescription &child : drawcall.children) - { - if(ShouldHide(child)) - continue; - - if(!(child.flags & DrawFlags::APICalls)) - { - onlyapi = false; - break; - } - } - - if(onlyapi) - return true; - } - } - - return false; -} - -QPair EventBrowser::AddDrawcalls(RDTreeWidgetItem *parent, - const rdcarray &draws) -{ - uint lastEID = 0, lastDraw = 0; - - for(int32_t i = 0; i < draws.count(); i++) - { - const DrawcallDescription &d = draws[i]; - - if(ShouldHide(d)) - continue; - - QVariant name = QString(d.name); - - RichResourceTextInitialise(name, &m_Ctx); - - RDTreeWidgetItem *child = new RDTreeWidgetItem( - {name, QString::number(d.eventId), QString::number(d.drawcallId), lit("---")}); - - QPair last = AddDrawcalls(child, d.children); - lastEID = last.first; - lastDraw = last.second; - - if(lastEID > d.eventId) - { - child->setText(COL_EID, QFormatStr("%1-%2").arg(d.eventId).arg(lastEID)); - child->setText(COL_DRAW, QFormatStr("%1-%2").arg(d.drawcallId).arg(lastDraw)); - } - - if(lastEID == 0) - { - lastEID = d.eventId; - lastDraw = d.drawcallId; - - if((draws[i].flags & DrawFlags::SetMarker) && i + 1 < draws.count()) - lastEID = draws[i + 1].eventId; - } - - child->setTag(QVariant::fromValue(EventItemTag(draws[i].eventId, lastEID))); - - if(m_Ctx.Config().EventBrowser_ApplyColors) - { - // if alpha isn't 0, assume the colour is valid - if((d.flags & (DrawFlags::PushMarker | DrawFlags::SetMarker)) && d.markerColor.w > 0.0f) - { - QColor col = QColor::fromRgb( - qRgb(d.markerColor.x * 255.0f, d.markerColor.y * 255.0f, d.markerColor.z * 255.0f)); - - child->setTreeColor(col); - - if(m_Ctx.Config().EventBrowser_ColorEventRow) - { - QColor textCol = ui->events->palette().color(QPalette::Text); - - child->setBackgroundColor(col); - child->setForegroundColor(contrastingColor(col, textCol)); - } - } - } - - parent->addChild(child); - } - - return qMakePair(lastEID, lastDraw); -} - -void EventBrowser::SetDrawcallTimes(RDTreeWidgetItem *node, const rdcarray &results) -{ - if(node == NULL) - return; - - // parent nodes take the value of the sum of their children - double duration = 0.0; - - // look up leaf nodes in the dictionary - if(node->childCount() == 0) - { - uint32_t eid = node->tag().value().EID; - - duration = -1.0; - - for(const CounterResult &r : results) - { - if(r.eventId == eid) - duration = r.value.d; - } - - double secs = duration; - - if(m_TimeUnit == TimeUnit::Milliseconds) - secs *= 1000.0; - else if(m_TimeUnit == TimeUnit::Microseconds) - secs *= 1000000.0; - else if(m_TimeUnit == TimeUnit::Nanoseconds) - secs *= 1000000000.0; - - node->setText(COL_DURATION, duration < 0.0f ? QString() : Formatter::Format(secs)); - EventItemTag tag = node->tag().value(); - tag.duration = duration; - node->setTag(QVariant::fromValue(tag)); - - return; - } - - for(int i = 0; i < node->childCount(); i++) - { - SetDrawcallTimes(node->child(i), results); - - double nd = node->child(i)->tag().value().duration; - - if(nd > 0.0) - duration += nd; - } - - double secs = duration; - - if(m_TimeUnit == TimeUnit::Milliseconds) - secs *= 1000.0; - else if(m_TimeUnit == TimeUnit::Microseconds) - secs *= 1000000.0; - else if(m_TimeUnit == TimeUnit::Nanoseconds) - secs *= 1000000000.0; - - node->setText(COL_DURATION, duration < 0.0f ? QString() : Formatter::Format(secs)); - EventItemTag tag = node->tag().value(); - tag.duration = duration; - node->setTag(QVariant::fromValue(tag)); + m_Model->RefreshCache(); } void EventBrowser::on_find_clicked() @@ -495,10 +912,17 @@ void EventBrowser::on_gotoEID_clicked() void EventBrowser::on_bookmark_clicked() { - RDTreeWidgetItem *n = ui->events->currentItem(); + QModelIndex idx = ui->events->currentIndex(); - if(n) - toggleBookmark(n->tag().value().lastEID); + if(idx.isValid()) + { + EventBookmark mark(GetSelectedEID(idx)); + + if(m_Ctx.GetBookmarks().contains(mark)) + m_Ctx.RemoveBookmark(mark.eventId); + else + m_Ctx.SetBookmark(mark); + } } void EventBrowser::on_timeDraws_clicked() @@ -508,52 +932,35 @@ void EventBrowser::on_timeDraws_clicked() ui->events->header()->showSection(COL_DURATION); m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { + m_Model->SetTimes(r->FetchCounters({GPUCounter::EventGPUDuration})); - m_Times = r->FetchCounters({GPUCounter::EventGPUDuration}); - - GUIInvoke::call(this, [this]() { - if(ui->events->topLevelItemCount() == 0) - return; - - SetDrawcallTimes(ui->events->topLevelItem(0), m_Times); - ui->events->update(); - }); + GUIInvoke::call(this, [this]() { ui->events->update(); }); }); } -void EventBrowser::on_events_currentItemChanged(RDTreeWidgetItem *current, RDTreeWidgetItem *previous) +void EventBrowser::events_currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { - if(previous) - { - EventItemTag tag = previous->tag().value(); - tag.current = false; - previous->setTag(QVariant::fromValue(tag)); - RefreshIcon(previous, tag); - } - - if(!current) + if(!current.isValid()) return; - EventItemTag tag = current->tag().value(); - tag.current = true; - current->setTag(QVariant::fromValue(tag)); - RefreshIcon(current, tag); + uint32_t selectedEID = GetSelectedEID(current); + uint32_t effectiveEID = GetEffectiveEID(current); - m_Ctx.SetEventID({this}, tag.EID, tag.lastEID); + m_Ctx.SetEventID({this}, selectedEID, effectiveEID); - RefreshShaderMessages(); + m_Model->RefreshCache(); - const DrawcallDescription *draw = m_Ctx.GetDrawcall(tag.lastEID); + const DrawcallDescription *draw = m_Ctx.GetDrawcall(effectiveEID); ui->stepPrev->setEnabled(draw && draw->previous); ui->stepNext->setEnabled(draw && draw->next); // special case for the first draw in the frame - if(tag.lastEID == 0) + if(effectiveEID == 0) ui->stepNext->setEnabled(true); // special case for the first 'virtual' draw at EID 0 - if(m_Ctx.GetFirstDrawcall() && tag.lastEID == m_Ctx.GetFirstDrawcall()->eventId) + if(m_Ctx.GetFirstDrawcall() && effectiveEID == m_Ctx.GetFirstDrawcall()->eventId) ui->stepPrev->setEnabled(true); highlightBookmarks(); @@ -566,8 +973,9 @@ void EventBrowser::on_HideFindJump() ui->jumpToEID->setText(QString()); - ClearFindIcons(); - ui->findEvent->setPalette(palette()); + ui->findEvent->setText(QString()); + m_Model->SetFindText(QString()); + updateFindResultsAvailable(); } void EventBrowser::on_jumpToEID_returnPressed() @@ -582,14 +990,9 @@ void EventBrowser::on_jumpToEID_returnPressed() void EventBrowser::findHighlight_timeout() { - ClearFindIcons(); - - int results = SetFindIcons(ui->findEvent->text()); - - if(results > 0) - ui->findEvent->setPalette(palette()); - else - ui->findEvent->setPalette(m_redPalette); + m_Model->SetFindText(ui->findEvent->text()); + SelectEvent(m_FilterModel->mapFromSource(m_Model->Find(true))); + updateFindResultsAvailable(); } void EventBrowser::on_findEvent_textEdited(const QString &arg1) @@ -598,8 +1001,8 @@ void EventBrowser::on_findEvent_textEdited(const QString &arg1) { m_FindHighlight->stop(); - ui->findEvent->setPalette(palette()); - ClearFindIcons(); + m_Model->SetFindText(QString()); + updateFindResultsAvailable(); } else { @@ -613,9 +1016,6 @@ void EventBrowser::on_findEvent_returnPressed() if(m_FindHighlight->isActive()) m_FindHighlight->stop(); - if(!ui->findEvent->text().isEmpty()) - Find(true); - findHighlight_timeout(); } @@ -628,9 +1028,8 @@ void EventBrowser::on_findEvent_keyPress(QKeyEvent *event) m_FindHighlight->stop(); if(!ui->findEvent->text().isEmpty()) - Find(event->modifiers() & Qt::ShiftModifier ? false : true); - - findHighlight_timeout(); + SelectEvent(m_FilterModel->mapFromSource( + m_Model->Find(event->modifiers() & Qt::ShiftModifier ? false : true))); event->accept(); } @@ -638,12 +1037,14 @@ void EventBrowser::on_findEvent_keyPress(QKeyEvent *event) void EventBrowser::on_findNext_clicked() { - Find(true); + SelectEvent(m_FilterModel->mapFromSource(m_Model->Find(true))); + updateFindResultsAvailable(); } void EventBrowser::on_findPrev_clicked() { - Find(false); + SelectEvent(m_FilterModel->mapFromSource(m_Model->Find(false))); + updateFindResultsAvailable(); } void EventBrowser::on_stepNext_clicked() @@ -699,12 +1100,15 @@ void EventBrowser::on_exportDraws_clicked() int maxNameLength = 0; - for(const DrawcallDescription &d : m_Ctx.CurDrawcalls()) - GetMaxNameLength(maxNameLength, 0, false, d); + QModelIndex root = ui->events->model()->index(0, COL_NAME); + + // skip child 0, which is the Capture Start entry that we don't want to include + for(int i = 1, rowCount = ui->events->model()->rowCount(root); i < rowCount; i++) + GetMaxNameLength(maxNameLength, 0, false, ui->events->model()->index(i, COL_NAME, root)); QString line = QFormatStr(" EID | %1 | Draw #").arg(lit("Event"), -maxNameLength); - if(!m_Times.empty()) + if(m_Model->HasTimes()) { line += QFormatStr(" | %1 (%2)").arg(tr("Duration")).arg(ToQStr(m_TimeUnit)); } @@ -713,7 +1117,7 @@ void EventBrowser::on_exportDraws_clicked() line = QFormatStr("--------%1-----------").arg(QString(), maxNameLength, QLatin1Char('-')); - if(!m_Times.empty()) + if(m_Model->HasTimes()) { int maxDurationLength = 0; maxDurationLength = qMax(maxDurationLength, Formatter::Format(1.0).length()); @@ -725,8 +1129,9 @@ void EventBrowser::on_exportDraws_clicked() stream << line << "\n"; - for(const DrawcallDescription &d : m_Ctx.CurDrawcalls()) - ExportDrawcall(stream, maxNameLength, 0, false, d); + for(int i = 1, rowCount = ui->events->model()->rowCount(root); i < rowCount; i++) + ExportDrawcall(stream, maxNameLength, 0, false, + ui->events->model()->index(i, COL_NAME, root)); } else { @@ -747,87 +1152,58 @@ void EventBrowser::on_exportDraws_clicked() void EventBrowser::on_colSelect_clicked() { - UpdateVisibleColumns(tr("Select Event Browser Columns"), COL_COUNT, ui->events->header(), - ui->events->getHeaders()); + QStringList headers; + for(int i = 0; i < ui->events->model()->columnCount(); i++) + headers << ui->events->model()->headerData(i, Qt::Horizontal).toString(); + UpdateVisibleColumns(tr("Select Event Browser Columns"), COL_COUNT, ui->events->header(), headers); } -QString EventBrowser::GetExportDrawcallString(int indent, bool firstchild, - const DrawcallDescription &drawcall) +QString EventBrowser::GetExportString(int indent, bool firstchild, const QModelIndex &idx) { QString prefix = QString(indent * 2 - (firstchild ? 1 : 0), QLatin1Char(' ')); if(firstchild) prefix += QLatin1Char('\\'); - return QFormatStr("%1- %2").arg(prefix).arg(drawcall.name); -} - -double EventBrowser::GetDrawTime(const DrawcallDescription &drawcall) -{ - if(!drawcall.children.empty()) - { - double total = 0.0; - - for(const DrawcallDescription &d : drawcall.children) - { - double f = GetDrawTime(d); - if(f >= 0) - total += f; - } - - return total; - } - - for(const CounterResult &r : m_Times) - { - if(r.eventId == drawcall.eventId) - return r.value.d; - } - - return -1.0; + return QFormatStr("%1- %2").arg(prefix).arg( + RichResourceTextFormat(m_Ctx, idx.data(Qt::DisplayRole))); } void EventBrowser::GetMaxNameLength(int &maxNameLength, int indent, bool firstchild, - const DrawcallDescription &drawcall) + const QModelIndex &idx) { - QString nameString = GetExportDrawcallString(indent, firstchild, drawcall); + QString nameString = GetExportString(indent, firstchild, idx); maxNameLength = qMax(maxNameLength, nameString.count()); firstchild = true; - for(const DrawcallDescription &d : drawcall.children) + for(int i = 0, rowCount = idx.model()->rowCount(idx); i < rowCount; i++) { - GetMaxNameLength(maxNameLength, indent + 1, firstchild, d); + GetMaxNameLength(maxNameLength, indent + 1, firstchild, idx.child(i, COL_NAME)); firstchild = false; } } void EventBrowser::ExportDrawcall(QTextStream &writer, int maxNameLength, int indent, - bool firstchild, const DrawcallDescription &drawcall) + bool firstchild, const QModelIndex &idx) { - QString eidString = drawcall.children.empty() ? QString::number(drawcall.eventId) : QString(); + QString nameString = GetExportString(indent, firstchild, idx); - QString nameString = GetExportDrawcallString(indent, firstchild, drawcall); + QModelIndex eidIdx = idx.model()->sibling(idx.row(), COL_EID, idx); + QModelIndex drawIdx = idx.model()->sibling(idx.row(), COL_DRAW, idx); QString line = QFormatStr("%1 | %2 | %3") - .arg(eidString, -5) + .arg(eidIdx.data(Qt::DisplayRole).toString(), -5) .arg(nameString, -maxNameLength) - .arg(drawcall.drawcallId, -6); + .arg(drawIdx.data(Qt::DisplayRole).toString(), -6); - if(!m_Times.empty()) + if(m_Model->HasTimes()) { - double f = GetDrawTime(drawcall); + QVariant duration = idx.model()->sibling(idx.row(), COL_DURATION, idx).data(Qt::DisplayRole); - if(f >= 0) + if(duration != QVariant()) { - if(m_TimeUnit == TimeUnit::Milliseconds) - f *= 1000.0; - else if(m_TimeUnit == TimeUnit::Microseconds) - f *= 1000000.0; - else if(m_TimeUnit == TimeUnit::Nanoseconds) - f *= 1000000000.0; - - line += QFormatStr(" | %1").arg(Formatter::Format(f)); + line += QFormatStr(" | %1").arg(duration.toString()); } else { @@ -839,9 +1215,9 @@ void EventBrowser::ExportDrawcall(QTextStream &writer, int maxNameLength, int in firstchild = true; - for(const DrawcallDescription &d : drawcall.children) + for(int i = 0, rowCount = idx.model()->rowCount(idx); i < rowCount; i++) { - ExportDrawcall(writer, maxNameLength, indent + 1, firstchild, d); + ExportDrawcall(writer, maxNameLength, indent + 1, firstchild, idx.child(i, COL_NAME)); firstchild = false; } } @@ -869,7 +1245,7 @@ QVariant EventBrowser::persistData() ui->events->header()->hideSection(i); // name is just informative - col[lit("name")] = ui->events->headerText(i); + col[lit("name")] = ui->events->model()->headerData(i, Qt::Horizontal); col[lit("index")] = ui->events->header()->visualIndex(i); col[lit("hidden")] = hidden; col[lit("size")] = size; @@ -914,9 +1290,10 @@ void EventBrowser::events_keyPress(QKeyEvent *event) if(event->key() == Qt::Key_F3) { if(event->modifiers() == Qt::ShiftModifier) - Find(false); + SelectEvent(m_FilterModel->mapFromSource(m_Model->Find(false))); else - Find(true); + SelectEvent(m_FilterModel->mapFromSource(m_Model->Find(true))); + updateFindResultsAvailable(); } if(event->modifiers() == Qt::ControlModifier) @@ -946,7 +1323,7 @@ void EventBrowser::events_keyPress(QKeyEvent *event) void EventBrowser::events_contextMenu(const QPoint &pos) { - RDTreeWidgetItem *item = ui->events->itemAt(pos); + QModelIndex index = ui->events->indexAt(pos); QMenu contextMenu(this); @@ -967,15 +1344,15 @@ void EventBrowser::events_contextMenu(const QPoint &pos) toggleBookmark.setIcon(Icons::asterisk_orange()); selectCols.setIcon(Icons::timeline_marker()); - expandAll.setEnabled(item && item->childCount() > 0); - collapseAll.setEnabled(item && item->childCount() > 0); + expandAll.setEnabled(index.isValid() && ui->events->model()->rowCount(index) > 0); + collapseAll.setEnabled(expandAll.isEnabled()); toggleBookmark.setEnabled(m_Ctx.IsCaptureLoaded()); QObject::connect(&expandAll, &QAction::triggered, - [this, item]() { ui->events->expandAllItems(item); }); + [this, index]() { ui->events->expandAll(index); }); QObject::connect(&collapseAll, &QAction::triggered, - [this, item]() { ui->events->collapseAllItems(item); }); + [this, index]() { ui->events->collapseAll(index); }); QObject::connect(&toggleBookmark, &QAction::triggered, this, &EventBrowser::on_bookmark_clicked); @@ -1034,17 +1411,6 @@ void EventBrowser::repopulateBookmarks() highlightBookmarks(); - RDTreeWidgetItem *found = NULL; - FindEventNode(found, ui->events->topLevelItem(0), EID); - - if(found) - { - EventItemTag tag = found->tag().value(); - tag.bookmark = true; - found->setTag(QVariant::fromValue(tag)); - RefreshIcon(found, tag); - } - m_BookmarkStripLayout->removeItem(m_BookmarkSpacer); m_BookmarkStripLayout->addWidget(but); m_BookmarkStripLayout->addItem(m_BookmarkSpacer); @@ -1058,31 +1424,12 @@ void EventBrowser::repopulateBookmarks() { delete m_BookmarkButtons[EID]; m_BookmarkButtons.remove(EID); - - RDTreeWidgetItem *found = NULL; - FindEventNode(found, ui->events->topLevelItem(0), EID); - - if(found) - { - EventItemTag tag = found->tag().value(); - tag.bookmark = false; - found->setTag(QVariant::fromValue(tag)); - RefreshIcon(found, tag); - } } } ui->bookmarkStrip->setVisible(!bookmarks.isEmpty()); -} -void EventBrowser::toggleBookmark(uint32_t EID) -{ - EventBookmark mark(EID); - - if(m_Ctx.GetBookmarks().contains(mark)) - m_Ctx.RemoveBookmark(EID); - else - m_Ctx.SetBookmark(mark); + m_Model->RefreshCache(); } void EventBrowser::jumpToBookmark(int idx) @@ -1106,97 +1453,17 @@ void EventBrowser::highlightBookmarks() } } -bool EventBrowser::hasBookmark(RDTreeWidgetItem *node) +void EventBrowser::ExpandNode(QModelIndex idx) { - if(node) - return hasBookmark(node->tag().value().EID); - - return false; -} - -bool EventBrowser::hasBookmark(uint32_t EID) -{ - return m_Ctx.GetBookmarks().contains(EventBookmark(EID)); -} - -void EventBrowser::RefreshIcon(RDTreeWidgetItem *item, EventItemTag tag) -{ - if(tag.current) - item->setIcon(COL_NAME, Icons::flag_green()); - else if(tag.bookmark) - item->setIcon(COL_NAME, Icons::asterisk_orange()); - else if(tag.find) - item->setIcon(COL_NAME, Icons::find()); - else - item->setIcon(COL_NAME, QIcon()); -} - -void EventBrowser::RefreshShaderMessages() -{ - uint32_t eventId = m_Ctx.CurEvent(); - - RDTreeWidgetItem *item = ui->events->currentItem(); - - if(item->tag().value().EID != eventId) - return; - - const DrawcallDescription *draw = m_Ctx.GetDrawcall(eventId); - const rdcarray &msgs = m_Ctx.CurPipelineState().GetShaderMessages(); - - if(draw) + QModelIndex i = idx; + while(idx.isValid()) { - QString name(draw->name); - - if(!msgs.empty()) - name += lit(" __rd_msgs::%1:%2").arg(draw->eventId).arg(msgs.count()); - - QVariant v = name; - - RichResourceTextInitialise(v, &m_Ctx); - - item->setText(0, v); - } -} - -bool EventBrowser::FindEventNode(RDTreeWidgetItem *&found, RDTreeWidgetItem *parent, uint32_t eventId) -{ - // do a reverse search to find the last match (in case of 'set' markers that - // inherit the event of the next real draw). - for(int i = parent->childCount() - 1; i >= 0; i--) - { - RDTreeWidgetItem *n = parent->child(i); - - uint nEID = n->tag().value().lastEID; - uint fEID = found ? found->tag().value().lastEID : 0; - - if(nEID >= eventId && (found == NULL || nEID <= fEID)) - found = n; - - if(nEID == eventId && n->childCount() == 0) - return true; - - if(n->childCount() > 0) - { - bool exact = FindEventNode(found, n, eventId); - if(exact) - return true; - } + ui->events->expand(idx); + idx = idx.parent(); } - return false; -} - -void EventBrowser::ExpandNode(RDTreeWidgetItem *node) -{ - RDTreeWidgetItem *n = node; - while(node != NULL) - { - ui->events->expandItem(node); - node = node->parent(); - } - - if(n) - ui->events->scrollToItem(n); + if(i.isValid()) + ui->events->scrollTo(i); } bool EventBrowser::SelectEvent(uint32_t eventId) @@ -1204,170 +1471,34 @@ bool EventBrowser::SelectEvent(uint32_t eventId) if(!m_Ctx.IsCaptureLoaded()) return false; - RDTreeWidgetItem *found = NULL; - FindEventNode(found, ui->events->topLevelItem(0), eventId); - if(found != NULL) + QModelIndex found = m_FilterModel->mapFromSource(m_Model->GetIndexForEID(eventId)); + if(found.isValid()) { - ui->events->setCurrentItem(found); - ui->events->setSelectedItem(found); + SelectEvent(found); - ExpandNode(found); return true; } return false; } -void EventBrowser::ClearFindIcons(RDTreeWidgetItem *parent) +void EventBrowser::SelectEvent(QModelIndex idx) { - for(int i = 0; i < parent->childCount(); i++) - { - RDTreeWidgetItem *n = parent->child(i); - - EventItemTag tag = n->tag().value(); - tag.find = false; - n->setTag(QVariant::fromValue(tag)); - RefreshIcon(n, tag); - - if(n->childCount() > 0) - ClearFindIcons(n); - } -} - -void EventBrowser::ClearFindIcons() -{ - if(m_Ctx.IsCaptureLoaded() && ui->events->topLevelItemCount() > 0) - ClearFindIcons(ui->events->topLevelItem(0)); -} - -int EventBrowser::SetFindIcons(RDTreeWidgetItem *parent, QString filter) -{ - int results = 0; - - for(int i = 0; i < parent->childCount(); i++) - { - RDTreeWidgetItem *n = parent->child(i); - - if(n->text(COL_NAME).contains(filter, Qt::CaseInsensitive)) - { - EventItemTag tag = n->tag().value(); - tag.find = true; - n->setTag(QVariant::fromValue(tag)); - RefreshIcon(n, tag); - results++; - } - - if(n->childCount() > 0) - { - results += SetFindIcons(n, filter); - } - } - - return results; -} - -int EventBrowser::SetFindIcons(QString filter) -{ - if(filter.isEmpty() || !m_Ctx.IsCaptureLoaded()) - return 0; - - return SetFindIcons(ui->events->topLevelItem(0), filter); -} - -RDTreeWidgetItem *EventBrowser::FindNode(RDTreeWidgetItem *parent, QString filter, uint32_t after) -{ - for(int i = 0; i < parent->childCount(); i++) - { - RDTreeWidgetItem *n = parent->child(i); - - uint eid = n->tag().value().lastEID; - - if(eid > after && n->text(COL_NAME).contains(filter, Qt::CaseInsensitive)) - return n; - - if(n->childCount() > 0) - { - RDTreeWidgetItem *found = FindNode(n, filter, after); - - if(found != NULL) - return found; - } - } - - return NULL; -} - -int EventBrowser::FindEvent(RDTreeWidgetItem *parent, QString filter, uint32_t after, bool forward) -{ - if(parent == NULL) - return -1; - - for(int i = forward ? 0 : parent->childCount() - 1; i >= 0 && i < parent->childCount(); - i += forward ? 1 : -1) - { - auto n = parent->child(i); - - uint eid = n->tag().value().lastEID; - - bool matchesAfter = (forward && eid > after) || (!forward && eid < after); - - if(matchesAfter) - { - QString name = n->text(COL_NAME); - if(name.contains(filter, Qt::CaseInsensitive)) - return (int)eid; - } - - if(n->childCount() > 0) - { - int found = FindEvent(n, filter, after, forward); - - if(found > 0) - return found; - } - } - - return -1; -} - -int EventBrowser::FindEvent(QString filter, uint32_t after, bool forward) -{ - if(!m_Ctx.IsCaptureLoaded()) - return 0; - - return FindEvent(ui->events->topLevelItem(0), filter, after, forward); -} - -void EventBrowser::Find(bool forward) -{ - if(ui->findEvent->text().isEmpty()) + if(!idx.isValid()) return; - uint32_t curEID = m_Ctx.CurSelectedEvent(); + ui->events->selectionModel()->setCurrentIndex( + idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - RDTreeWidgetItem *node = ui->events->selectedItem(); - if(node) - curEID = node->tag().value().lastEID; + ExpandNode(idx); +} - int eid = FindEvent(ui->findEvent->text(), curEID, forward); - if(eid >= 0) - { - SelectEvent((uint32_t)eid); +void EventBrowser::updateFindResultsAvailable() +{ + if(m_Model->NumFindResults() > 0 || ui->findEvent->text().isEmpty()) ui->findEvent->setPalette(palette()); - } - else // if(WrapSearch) - { - eid = FindEvent(ui->findEvent->text(), forward ? 0 : ~0U, forward); - if(eid >= 0) - { - SelectEvent((uint32_t)eid); - ui->findEvent->setPalette(palette()); - } - else - { - ui->findEvent->setPalette(m_redPalette); - } - } + else + ui->findEvent->setPalette(m_redPalette); } void EventBrowser::UpdateDurationColumn() @@ -1377,8 +1508,5 @@ void EventBrowser::UpdateDurationColumn() m_TimeUnit = m_Ctx.Config().EventBrowser_TimeUnit; - ui->events->setHeaderText(COL_DURATION, tr("Duration (%1)").arg(UnitSuffix(m_TimeUnit))); - - if(!m_Times.empty()) - SetDrawcallTimes(ui->events->topLevelItem(0), m_Times); + m_Model->UpdateDurationColumn(); } diff --git a/qrenderdoc/Windows/EventBrowser.h b/qrenderdoc/Windows/EventBrowser.h index 85051f5c8..4f1c16be1 100644 --- a/qrenderdoc/Windows/EventBrowser.h +++ b/qrenderdoc/Windows/EventBrowser.h @@ -35,12 +35,15 @@ class EventBrowser; class QSpacerItem; class QToolButton; -class RDTreeWidgetItem; class QTimer; class QTextStream; class FlowLayout; struct EventItemTag; +class RichTextViewDelegate; +struct EventItemModel; +struct EventFilterModel; + class EventBrowser : public QFrame, public IEventBrowser, public ICaptureViewer { private: @@ -75,7 +78,6 @@ private slots: void on_findEvent_returnPressed(); void on_findEvent_keyPress(QKeyEvent *event); void on_findEvent_textEdited(const QString &arg1); - void on_events_currentItemChanged(RDTreeWidgetItem *current, RDTreeWidgetItem *previous); void on_findNext_clicked(); void on_findPrev_clicked(); void on_stepNext_clicked(); @@ -87,51 +89,38 @@ private slots: void findHighlight_timeout(); void events_keyPress(QKeyEvent *event); void events_contextMenu(const QPoint &pos); - -public slots: - void clearBookmarks(); - bool hasBookmark(uint32_t EID); - void toggleBookmark(uint32_t EID); - void jumpToBookmark(int idx); + void events_currentChanged(const QModelIndex ¤t, const QModelIndex &previous); private: - bool ShouldHide(const DrawcallDescription &drawcall); - QPair AddDrawcalls(RDTreeWidgetItem *parent, - const rdcarray &draws); - void SetDrawcallTimes(RDTreeWidgetItem *node, const rdcarray &results); + void ExpandNode(QModelIndex idx); - void ExpandNode(RDTreeWidgetItem *node); - - bool FindEventNode(RDTreeWidgetItem *&found, RDTreeWidgetItem *parent, uint32_t eventId); bool SelectEvent(uint32_t eventId); + void SelectEvent(QModelIndex found); - void ClearFindIcons(RDTreeWidgetItem *parent); - void ClearFindIcons(); - - int SetFindIcons(RDTreeWidgetItem *parent, QString filter); - int SetFindIcons(QString filter); + void updateFindResultsAvailable(); + void clearBookmarks(); + void jumpToBookmark(int idx); void repopulateBookmarks(); void highlightBookmarks(); - bool hasBookmark(RDTreeWidgetItem *node); - RDTreeWidgetItem *FindNode(RDTreeWidgetItem *parent, QString filter, uint32_t after); - int FindEvent(RDTreeWidgetItem *parent, QString filter, uint32_t after, bool forward); + int FindEvent(QModelIndex parent, QString filter, uint32_t after, bool forward); int FindEvent(QString filter, uint32_t after, bool forward); void Find(bool forward); - QString GetExportDrawcallString(int indent, bool firstchild, const DrawcallDescription &drawcall); - double GetDrawTime(const DrawcallDescription &drawcall); - void GetMaxNameLength(int &maxNameLength, int indent, bool firstchild, - const DrawcallDescription &drawcall); + QString GetExportString(int indent, bool firstchild, const QModelIndex &idx); + void GetMaxNameLength(int &maxNameLength, int indent, bool firstchild, const QModelIndex &idx); void ExportDrawcall(QTextStream &writer, int maxNameLength, int indent, bool firstchild, - const DrawcallDescription &drawcall); + const QModelIndex &idx); QPalette m_redPalette; - TimeUnit m_TimeUnit = TimeUnit::Count; + RichTextViewDelegate *m_delegate = NULL; - rdcarray m_Times; + EventItemModel *m_Model; + EventFilterModel *m_FilterModel; + + TimeUnit m_TimeUnit = TimeUnit::Count; QTimer *m_FindHighlight; @@ -139,9 +128,7 @@ private: QSpacerItem *m_BookmarkSpacer; QMap m_BookmarkButtons; - void RefreshIcon(RDTreeWidgetItem *item, EventItemTag tag); void RefreshShaderMessages(); - Ui::EventBrowser *ui; ICaptureContext &m_Ctx; }; diff --git a/qrenderdoc/Windows/EventBrowser.ui b/qrenderdoc/Windows/EventBrowser.ui index 56121bb98..b1d1f378f 100644 --- a/qrenderdoc/Windows/EventBrowser.ui +++ b/qrenderdoc/Windows/EventBrowser.ui @@ -467,7 +467,7 @@ - + QFrame::Box @@ -510,9 +510,9 @@ - RDTreeWidget + RDTreeView QTreeView -
Widgets/Extended/RDTreeWidget.h
+
Widgets/Extended/RDTreeView.h
RDLineEdit diff --git a/renderdoc/api/replay/rdcarray.h b/renderdoc/api/replay/rdcarray.h index b44270d6b..af8fe3764 100644 --- a/renderdoc/api/replay/rdcarray.h +++ b/renderdoc/api/replay/rdcarray.h @@ -186,6 +186,7 @@ public: { return usedCount == o.usedCount && ItemHelper::equalRange(elems, o.elems, usedCount); } + bool operator!=(const rdcarray &o) const { return !(*this == o); } bool operator<(const rdcarray &o) const { if(usedCount != o.usedCount)