From aecf6870981a2b5eb593beb2852538369569b4d6 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 10 Feb 2021 17:36:50 +0000 Subject: [PATCH] Change event browser model to be event-based --- qrenderdoc/Code/Interface/QRDInterface.h | 25 ++ qrenderdoc/Windows/APIInspector.cpp | 69 ++-- qrenderdoc/Windows/APIInspector.h | 1 + qrenderdoc/Windows/EventBrowser.cpp | 334 +++++++++++++++--- qrenderdoc/Windows/EventBrowser.h | 3 + renderdoc/driver/d3d11/d3d11_context.cpp | 5 + renderdoc/driver/d3d11/d3d11_context_wrap.cpp | 2 + renderdoc/driver/d3d12/d3d12_commands.cpp | 12 +- renderdoc/driver/gl/gl_driver.cpp | 5 + renderdoc/driver/vulkan/vk_core.cpp | 13 +- renderdoc/replay/replay_driver.cpp | 3 + 11 files changed, 381 insertions(+), 91 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index b19af84d4..c88a50bbe 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -209,6 +209,31 @@ QWidget. DOCUMENT("Updates the duration column if the selected time unit changes."); virtual void UpdateDurationColumn() = 0; + DOCUMENT(R"(Uses the existing caching in the event browser to return a :class:`~renderdoc.APIEvent` +for a specified EID. + +If no capture is loaded or the EID doesn't correspond to a known event, an empty struct will be +returned. + +:param int eventId: The EID to look up. +:return: The event corresponding to the EID, or an empty struct if no such EID exists. +:rtype: renderdoc.APIEvent +)"); + virtual APIEvent GetAPIEventForEID(uint32_t eventId) = 0; + + DOCUMENT(R"(Uses the existing caching in the event browser to return a +:class:`~renderdoc.DrawcallDescription` for a specified EID. This draw may not be the exact EID +specified, but it will be the draw that the EID is associated with. I.e. if you specify the EID for +a state setting event the next draw will be returned. + +If no capture is loaded or the EID doesn't correspond to a known event, ``None`` will be returned. + +:param int eventId: The EID to look up. +:return: The drawcall containing the EID, or ``None`` if no such EID exists. +:rtype: renderdoc.DrawcallDescription +)"); + virtual const DrawcallDescription *GetDrawcallForEID(uint32_t eventId) = 0; + protected: IEventBrowser() = default; ~IEventBrowser() = default; diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp index e1a716465..abb6cd437 100644 --- a/qrenderdoc/Windows/APIInspector.cpp +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -206,42 +206,55 @@ void APIInspector::fillAPIView() m_Chunks.clear(); - const SDFile &file = m_Ctx.GetStructuredFile(); const DrawcallDescription *draw = m_Ctx.CurSelectedDrawcall(); if(draw != NULL && !draw->events.isEmpty()) { for(const APIEvent &ev : draw->events) { - RDTreeWidgetItem *root = new RDTreeWidgetItem({QString::number(ev.eventId), QString()}); - - SDChunk *chunk = NULL; - - if(ev.chunkIndex < file.chunks.size()) - { - chunk = file.chunks[ev.chunkIndex]; - - m_Chunks.push_back(chunk); - - root->setText(1, chunk->name); - - addStructuredChildren(root, *chunk); - } - else - { - root->setText(1, tr("Invalid chunk index %1").arg(ev.chunkIndex)); - } - - if(ev.eventId == draw->eventId) - root->setBold(true); - - root->setTag(QVariant::fromValue((quintptr)(void *)chunk)); - - ui->apiEvents->addTopLevelItem(root); - - ui->apiEvents->setSelectedItem(root); + addEvent(ev, ev.eventId == draw->eventId); } } + else + { + APIEvent ev = m_Ctx.GetEventBrowser()->GetAPIEventForEID(m_Ctx.CurSelectedEvent()); + + if(ev.eventId > 0) + addEvent(ev, true); + } ui->apiEvents->setUpdatesEnabled(true); } + +void APIInspector::addEvent(const APIEvent &ev, bool primary) +{ + const SDFile &file = m_Ctx.GetStructuredFile(); + + RDTreeWidgetItem *root = new RDTreeWidgetItem({QString::number(ev.eventId), QString()}); + + SDChunk *chunk = NULL; + + if(ev.chunkIndex < file.chunks.size()) + { + chunk = file.chunks[ev.chunkIndex]; + + m_Chunks.push_back(chunk); + + root->setText(1, chunk->name); + + addStructuredChildren(root, *chunk); + } + else + { + root->setText(1, tr("Invalid chunk index %1").arg(ev.chunkIndex)); + } + + if(primary) + root->setBold(true); + + root->setTag(QVariant::fromValue((quintptr)(void *)chunk)); + + ui->apiEvents->addTopLevelItem(root); + + ui->apiEvents->setSelectedItem(root); +} diff --git a/qrenderdoc/Windows/APIInspector.h b/qrenderdoc/Windows/APIInspector.h index 891c08c20..aa7759313 100644 --- a/qrenderdoc/Windows/APIInspector.h +++ b/qrenderdoc/Windows/APIInspector.h @@ -65,4 +65,5 @@ private: void addCallstack(rdcarray calls); void fillAPIView(); + void addEvent(const APIEvent &ev, bool primary); }; diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 0702d73c3..25361f1ba 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -82,6 +82,9 @@ struct EventItemModel : public QAbstractItemModel emit endResetModel(); m_Nodes.clear(); + m_RowInParentCache.clear(); + m_EIDNameCache.clear(); + m_Draws.clear(); if(!m_Ctx.CurDrawcalls().empty()) m_Nodes[0] = CreateDrawNode(NULL); @@ -99,6 +102,7 @@ struct EventItemModel : public QAbstractItemModel uint32_t eid = m_Ctx.CurSelectedEvent(); m_MessageCounts[eid] = m_Ctx.CurPipelineState().GetShaderMessages().count(); + m_EIDNameCache.remove(eid); if(eid != data(m_CurrentEID, ROLE_SELECTED_EID) || eid == 0) { @@ -247,28 +251,79 @@ struct EventItemModel : public QAbstractItemModel if(eid == 0) return createIndex(0, 0, TagCaptureStart); - const DrawcallDescription *draw = m_Ctx.GetDrawcall(eid); + const DrawcallDescription *draw = m_Draws[eid]; if(draw) { + // this function is not called regularly (anywhere to do with painting) but only occasionally + // to find an index by EID, so we do an 'expensive' search for the row in the parent. The + // cache is LRU and holds a few entries in case the user is jumping back and forth between a + // few. + for(size_t i = 0; i < m_RowInParentCache.size(); i++) + { + if(m_RowInParentCache[i].first == eid) + return createIndex(m_RowInParentCache[i].second, 0, eid); + } + + const int MaxCacheSize = 10; + + // oldest entry is on the back, pop it + if(m_RowInParentCache.size() == MaxCacheSize) + m_RowInParentCache.pop_back(); + + // account for the Capture Start row we'll add at the top level + int rowInParent = draw->parent ? 0 : 1; + const rdcarray &draws = draw->parent ? draw->parent->children : m_Ctx.CurDrawcalls(); - int rowInParent = int(draw - draws.begin()); + for(const DrawcallDescription &d : draws) + { + // the first draw with an EID greater than the one we're searching for should contain it. + if(d.eventId >= eid) + { + // keep counting until we get to the row within this draw + for(size_t i = 0; i < d.events.size(); i++) + { + if(d.events[i].eventId < eid) + { + rowInParent++; + continue; + } - // add 1 to account for Capture Start - if(draw->parent == NULL) - rowInParent++; + if(d.events[i].eventId != eid) + qCritical() << "Couldn't find event" << eid << "within draw" << draw->eventId; - return createIndex(rowInParent, 0, (void *)draw); + // stop, we should be at the event now + break; + } + + break; + } + + rowInParent += d.events.count(); + } + + // insert the new element on the front of the cache + m_RowInParentCache.insert(0, {eid, rowInParent}); + + return createIndex(rowInParent, 0, eid); } else { - qWarning() << "Couldn't find draw for event" << eid; + qCritical() << "Couldn't find draw for event" << eid; } return QModelIndex(); } + const DrawcallDescription *GetDrawcallForEID(uint32_t eid) + { + if(eid < m_Draws.size()) + return m_Draws[eid]; + + return NULL; + } + ////////////////////////////////////////////////////////////////////////////////////// // // QAbstractItemModel methods @@ -289,11 +344,8 @@ struct EventItemModel : public QAbstractItemModel 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]); + // the rest are in the root draw node + return GetIndexForDrawChildRow(m_Nodes[0], row, column); } else if(parent.internalId() == TagCaptureStart) { @@ -302,11 +354,10 @@ struct EventItemModel : public QAbstractItemModel } 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]); + auto it = m_Nodes.find(parent.internalId()); + if(it != m_Nodes.end()) + return GetIndexForDrawChildRow(*it, row, column); } return QModelIndex(); @@ -322,7 +373,7 @@ struct EventItemModel : public QAbstractItemModel return createIndex(0, 0, TagRoot); // otherwise it's a draw - const DrawcallDescription *draw = (const DrawcallDescription *)index.internalPointer(); + const DrawcallDescription *draw = m_Draws[index.internalId()]; // if it has no parent draw, the parent is the root if(draw->parent == NULL) @@ -346,17 +397,22 @@ struct EventItemModel : public QAbstractItemModel if(parent.column() != 0) return 0; - // +1 for the capture start if(parent.internalId() == TagRoot) - return m_Ctx.CurDrawcalls().count() + 1; + return m_Nodes[0].rowCount; if(parent.internalId() == TagCaptureStart) return 0; - // otherwise it's a draw - const DrawcallDescription *draw = (const DrawcallDescription *)parent.internalPointer(); + // otherwise it's an event + uint32_t eid = (uint32_t)parent.internalId(); - return draw->children.count(); + // if it's a node, return the row count + auto it = m_Nodes.find(eid); + if(it != m_Nodes.end()) + return it->rowCount; + + // no other nodes have children + return 0; } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return COL_COUNT; } @@ -434,45 +490,31 @@ struct EventItemModel : public QAbstractItemModel } else { - const DrawcallDescription *draw = (const DrawcallDescription *)index.internalPointer(); + uint32_t eid = index.internalId(); if(role == ROLE_SELECTED_EID) - return draw->eventId; + return eid; if(role == ROLE_EFFECTIVE_EID) { - auto it = m_Nodes.find(draw->eventId); + auto it = m_Nodes.find(eid); if(it != m_Nodes.end()) return it->effectiveEID; - return draw->eventId; + return eid; } if(index.column() == COL_DURATION && role == Qt::DisplayRole) - { - return FormatDuration(draw->eventId); - } + return FormatDuration(eid); if(role == Qt::DisplayRole) { + const DrawcallDescription *draw = m_Draws[eid]; + 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; + case COL_NAME: return GetCachedEIDName(eid); + case COL_EID: return eid; + case COL_DRAW: return draw->eventId == eid ? QVariant(draw->drawcallId) : QVariant(); default: break; } } @@ -484,6 +526,12 @@ struct EventItemModel : public QAbstractItemModel if(!m_Ctx.Config().EventBrowser_ColorEventRow && role != RDTreeView::TreeLineColorRole) return QVariant(); + const DrawcallDescription *draw = m_Draws[eid]; + + // skip events that aren't the actual draw + if(draw->eventId != eid) + return QVariant(); + // if alpha isn't 0, assume the colour is valid if((draw->flags & (DrawFlags::PushMarker | DrawFlags::SetMarker)) && draw->markerColor.w > 0.0f) @@ -509,8 +557,8 @@ private: QAbstractItemView *m_View; - static const quintptr TagRoot = 0x0; - static const quintptr TagCaptureStart = 0x1; + static const quintptr TagRoot = quintptr(UINTPTR_MAX); + static const quintptr TagCaptureStart = quintptr(0); rdcarray m_Times; TimeUnit m_TimeUnit = TimeUnit::Count; @@ -523,11 +571,18 @@ private: 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 + // captures could have a lot of events, 1 million is high but not unreasonable and certainly not + // an upper bound. We store only 1 pointer per event which gives us reasonable memory usage (i.e. + // 8MB for that 1-million case on 64-bit) and still lets us look up what we need in O(1) and avoid + // more expensive lookups when we need the properties for an event. + // This gives us a pointer for every event ID pointing to the draw that contains it. + rdcarray m_Draws; + + // we can have a bigger structure for every nested node (i.e. draw with children). + // This drastically limits how many we need to worry about - some hierarchies have more nodes than + // others but even in hierarchies with lots of nodes the number is small, compared to + // draws/events. + // we cache this with a depth-first search at init time while populating m_Draws struct DrawTreeNode { const DrawcallDescription *draw; @@ -535,9 +590,28 @@ private: // cache the index for this node to make parent() significantly faster QModelIndex index; + + // this is the number of child events, meaning all the draws and all of their events, but *not* + // the events in any of their children. + uint32_t rowCount; + + // this is a cache of row index to draw. Rather than being present for every row, this is + // spaced out such that there are roughly Row2EIDFactor entries at most. This means that the + // O(n) lookup to find the EID for a given row has to process that many times less entries since + // it can jump to somewhere nearby. + // + // The key is the row, the value is the index in the list of children of the draw + QMap row2draw; + static const int Row2DrawFactor = 100; }; QMap m_Nodes; + // a cache of EID -> row in parent for looking up indices for arbitrary EIDs. + rdcarray> m_RowInParentCache; + + // needs to be mutable because we update this inside data() + mutable QMap m_EIDNameCache; + void AccumulateFindResults(QModelIndex root) { for(int i = 0, count = rowCount(root); i < count; i++) @@ -607,22 +681,38 @@ private: const rdcarray &drawRange = draw ? draw->children : m_Ctx.CurDrawcalls(); // account for the Capture Start row we'll add at the top level - int rowOffset = draw ? 0 : 1; + int row = draw ? 0 : 1; DrawTreeNode ret; ret.draw = draw; ret.effectiveEID = drawRange.back().eventId; + ret.row2draw[0] = 0; + + uint32_t row2eidStride = (drawRange.count() / DrawTreeNode::Row2DrawFactor) + 1; + for(int i = 0; i < drawRange.count(); i++) { const DrawcallDescription &d = drawRange[i]; + + if((i % row2eidStride) == 0) + ret.row2draw[row] = i; + + for(const APIEvent &e : d.events) + { + m_Draws.resize_for_index(e.eventId); + m_Draws[e.eventId] = &d; + } + + row += d.events.count(); + if(d.children.empty()) continue; DrawTreeNode node = CreateDrawNode(&d); - node.index = createIndex(i + rowOffset, 0, (void *)&d); + node.index = createIndex(row - 1, 0, d.eventId); if(d.eventId == ret.effectiveEID) ret.effectiveEID = node.effectiveEID; @@ -630,8 +720,121 @@ private: m_Nodes[d.eventId] = node; } + ret.rowCount = row; + return ret; } + + QModelIndex GetIndexForDrawChildRow(const DrawTreeNode &node, int row, int column) const + { + // if the row is out of bounds, bail + if(row < 0 || (uint32_t)row >= node.rowCount) + return QModelIndex(); + + // we do the linear 'counting' within the draws list to find the event at the given row. It's + // still essentially O(n) however we keep a limited cached of skip entries at each draw node + // which helps lower the cost significantly (by a factor of the roughly number of skip entries + // we store). + + const rdcarray &draws = + node.draw ? node.draw->children : m_Ctx.CurDrawcalls(); + int curRow = 0; + size_t curDraw = 0; + + // lowerBound doesn't do exactly what we want, we want the first row that is less-equal. So + // instead we use upperBound and go back one step if we don't get returned the first entry + auto it = node.row2draw.upperBound(row); + if(it != node.row2draw.begin()) + --it; + + // start at the first skip before the desired row + curRow = it.key(); + curDraw = it.value(); + + if(curRow > row) + { + // we should always have a skip, even if it's only the first child + qCritical() << "Couldn't find skip for" << row << "in draw node" + << (node.draw ? node.draw->eventId : 0); + + // start at the first child row instead + curRow = 0; + curDraw = 0; + } + + // if this draw doesn't contain the desired row, advance + while(curDraw < draws.size() && curRow + draws[curDraw].events.count() <= row) + { + curRow += draws[curDraw].events.count(); + curDraw++; + } + + // we've iterated all the draws and didn't come up with this row - but we checked above that + // we're in bounds of rowCount so something went wrong + if(curDraw >= draws.size()) + { + qCritical() << "Couldn't find draw containing row" << row << "in draw node" + << (node.draw ? node.draw->eventId : 0); + return QModelIndex(); + } + + // now curDraw contains the row at the idx'th event + int idx = row - curRow; + + if(idx < 0 || idx >= draws[curDraw].events.count()) + { + qCritical() << "Got invalid relative index for row" << row << "in draw node" + << (node.draw ? node.draw->eventId : 0); + return QModelIndex(); + } + + return createIndex(row, column, draws[curDraw].events[idx].eventId); + } + + QVariant GetCachedEIDName(uint32_t eid) const + { + auto it = m_EIDNameCache.find(eid); + if(it != m_EIDNameCache.end()) + return it.value(); + + const DrawcallDescription *draw = m_Draws[eid]; + + QString name; + + if(draw->eventId == eid) + { + name = draw->name; + } + else + { + for(const APIEvent &e : draw->events) + { + if(e.eventId == eid) + { + name = m_Ctx.GetStructuredFile().chunks[e.chunkIndex]->name; + break; + } + } + + if(name.isEmpty()) + qCritical() << "Couldn't find APIEvent for" << eid; + } + + if(m_MessageCounts.contains(eid)) + { + int count = m_MessageCounts[eid]; + if(count > 0) + name += lit(" __rd_msgs::%1:%2").arg(eid).arg(count); + } + + QVariant v = name; + + RichResourceTextInitialise(v, &m_Ctx); + + m_EIDNameCache[eid] = v; + + return v; + } }; struct EventFilterModel : public QSortFilterProxyModel @@ -1490,3 +1693,24 @@ void EventBrowser::UpdateDurationColumn() m_Model->UpdateDurationColumn(); } + +APIEvent EventBrowser::GetAPIEventForEID(uint32_t eid) +{ + const DrawcallDescription *draw = GetDrawcallForEID(eid); + + if(draw) + { + for(const APIEvent &ev : draw->events) + { + if(ev.eventId == eid) + return ev; + } + } + + return APIEvent(); +} + +const DrawcallDescription *EventBrowser::GetDrawcallForEID(uint32_t eid) +{ + return m_Model->GetDrawcallForEID(eid); +} diff --git a/qrenderdoc/Windows/EventBrowser.h b/qrenderdoc/Windows/EventBrowser.h index 4f1c16be1..b783c7e2c 100644 --- a/qrenderdoc/Windows/EventBrowser.h +++ b/qrenderdoc/Windows/EventBrowser.h @@ -58,6 +58,9 @@ public: // IEventBrowser QWidget *Widget() override { return this; } void UpdateDurationColumn() override; + APIEvent GetAPIEventForEID(uint32_t eid) override; + const DrawcallDescription *GetDrawcallForEID(uint32_t eid) override; + // ICaptureViewer void OnCaptureLoaded() override; void OnCaptureClosed() override; diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index c388f2164..ea1479b64 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -1150,6 +1150,11 @@ void WrappedID3D11DeviceContext::AddDrawcall(const DrawcallDescription &d, bool draw.events = m_CurEvents; m_CurEvents.clear(); } + else + { + draw.events.push_back(m_CurEvents.back()); + m_CurEvents.pop_back(); + } AddUsage(draw); diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index b56b22333..e3bfe16b0 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -5186,6 +5186,8 @@ bool WrappedID3D11DeviceContext::Serialise_ExecuteCommandList(SerialiserType &se if(IsLoading(m_State)) { + AddEvent(); + DrawcallDescription draw; draw.name = StringFormat::Fmt("ExecuteCommandList(List %s)", ToStr(CommandList).c_str()); draw.flags |= DrawFlags::CmdList; diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index 466367952..9591919e9 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -1887,15 +1887,19 @@ void D3D12CommandData::AddDrawcall(const DrawcallDescription &d, bool hasEvents, m_RootDrawcallID++; } + rdcarray &srcEvents = + m_LastCmdListID != ResourceId() ? m_BakedCmdListInfo[m_LastCmdListID].curEvents : m_RootEvents; + if(hasEvents) { - rdcarray &srcEvents = m_LastCmdListID != ResourceId() - ? m_BakedCmdListInfo[m_LastCmdListID].curEvents - : m_RootEvents; - draw.events = srcEvents; srcEvents.clear(); } + else + { + draw.events.push_back(srcEvents.back()); + srcEvents.pop_back(); + } // should have at least the root drawcall here, push this drawcall // onto the back's children list. diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index c7a9284eb..4518c4ad9 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -5602,6 +5602,11 @@ void WrappedOpenGL::AddDrawcall(const DrawcallDescription &d, bool hasEvents) draw.events = m_CurEvents; m_CurEvents.clear(); } + else + { + draw.events.push_back(m_CurEvents.back()); + m_CurEvents.pop_back(); + } AddUsage(draw); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 6dbbc9997..0e8579d8f 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -4168,15 +4168,20 @@ void WrappedVulkan::AddDrawcall(const DrawcallDescription &d, bool hasEvents) m_RootDrawcallID++; } + rdcarray &srcEvents = m_LastCmdBufferID != ResourceId() + ? m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents + : m_RootEvents; + if(hasEvents) { - rdcarray &srcEvents = m_LastCmdBufferID != ResourceId() - ? m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents - : m_RootEvents; - draw.events = srcEvents; srcEvents.clear(); } + else + { + draw.events.push_back(srcEvents.back()); + srcEvents.pop_back(); + } // should have at least the root drawcall here, push this drawcall // onto the back's children list. diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index a31dadb3c..452e2cd59 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -86,6 +86,9 @@ static DrawcallDescription *SetupDrawcallPointers(rdcarrayevents.empty() && draw->events.back().eventId == draw->eventId); + draw->parent = parent; if(!draw->children.empty())