diff --git a/docs/python_api/examples/renderdoc/iter_draws.py b/docs/python_api/examples/renderdoc/iter_draws.py index df4700541..af717371f 100644 --- a/docs/python_api/examples/renderdoc/iter_draws.py +++ b/docs/python_api/examples/renderdoc/iter_draws.py @@ -1,8 +1,5 @@ import renderdoc as rd -# Set up a hash for storing draw information by event -draws = {} - # Define a recursive function for iterating over draws def iterDraw(d, indent = ''): global draws @@ -10,16 +7,11 @@ def iterDraw(d, indent = ''): # Print this drawcall print('%s%d: %s' % (indent, d.eventId, d.name)) - # Save the draw by eventId for use later - draws[d.eventId] = d - # Iterate over the draw's children for d in d.children: iterDraw(d, indent + ' ') def sampleCode(controller): - global draws - # Iterate over all of the root drawcalls for d in controller.GetDrawcalls(): iterDraw(d) @@ -54,9 +46,9 @@ def sampleCode(controller): inpass = True # Advance to the next drawcall - if not draw.next in draws: + draw = draw.next + if draw is None: break - draw = draws[draw.next] if inpass: print("Pass #%d contained %d draws" % (passnum, passcontents)) @@ -82,7 +74,7 @@ def loadCapture(filename): if status != rd.ReplayStatus.Succeeded: raise RuntimeError("Couldn't initialise replay: " + str(status)) - return controller + return cap,controller if 'pyrenderdoc' in globals(): pyrenderdoc.Replay().BlockInvoke(sampleCode) diff --git a/docs/python_api/examples/renderdoc/iter_draws.rst b/docs/python_api/examples/renderdoc/iter_draws.rst index dc4b8b845..425a11b21 100644 --- a/docs/python_api/examples/renderdoc/iter_draws.rst +++ b/docs/python_api/examples/renderdoc/iter_draws.rst @@ -5,9 +5,9 @@ In this example we will show how to iterate over drawcalls. The drawcalls returned from :py:meth:`~renderdoc.ReplayController.GetDrawcalls` are draws or marker regions at the root level - with no parent marker region. There are multiple ways to iterate through the list of draws. -The first way illustrated in this sample is to walk the tree using :py:attr:`~renderdoc.DrawcallDescription.children`, which contains the list of child draws at any point in the tree. There is also :py:attr:`~renderdoc.DrawcallDescription.parent` which contains the index of the parent drawcall. +The first way illustrated in this sample is to walk the tree using :py:attr:`~renderdoc.DrawcallDescription.children`, which contains the list of child draws at any point in the tree. There is also :py:attr:`~renderdoc.DrawcallDescription.parent` which points to the parent drawcall. -The second is to use :py:attr:`~renderdoc.DrawcallDescription.previous` and :py:attr:`~renderdoc.DrawcallDescription.next`, which contain the indices of the previous and next draw respectively in a linear fashion, regardless of nesting depth. +The second is to use :py:attr:`~renderdoc.DrawcallDescription.previous` and :py:attr:`~renderdoc.DrawcallDescription.next`, which point to the previous and next draw respectively in a linear fashion, regardless of nesting depth. In the example we use this iteration to determine the number of passes, using the drawcall flags to denote the start of each pass by a starting clear call. diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 2653f68a8..629b2fe8d 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -74,6 +74,8 @@ CaptureContext::CaptureContext(QString paramFilename, QString remoteHost, uint32 m_CurVulkanPipelineState = NULL; m_CurPipelineState = &m_DummyPipelineState; + m_Drawcalls = &m_EmptyDraws; + m_StructuredFile = &m_DummySDFile; qApp->setApplicationVersion(QString::fromLatin1(RENDERDOC_GetVersionString())); @@ -193,8 +195,8 @@ void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFi // make sure we're on a consistent event before invoking viewer forms if(m_LastDrawcall) SetEventID(viewers, m_LastDrawcall->eventId, true); - else if(!m_Drawcalls.empty()) - SetEventID(viewers, m_Drawcalls.back().eventId, true); + else if(!m_Drawcalls->empty()) + SetEventID(viewers, m_Drawcalls->back().eventId, true); GUIInvoke::blockcall(m_MainWindow, [&viewers]() { // notify all the registers viewers that a capture has been loaded @@ -267,21 +269,22 @@ void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const QStri // fetch initial data like drawcalls, textures and buffers m_Renderer.BlockInvoke([this](IReplayController *r) { + if(Config().EventBrowser_AddFake) + r->AddFakeMarkers(); + m_FrameInfo = r->GetFrameInfo(); m_APIProps = r->GetAPIProperties(); m_PostloadProgress = 0.2f; - m_Drawcalls = r->GetDrawcalls(); + m_Drawcalls = &r->GetDrawcalls(); - AddFakeProfileMarkers(); - - m_FirstDrawcall = &m_Drawcalls[0]; + m_FirstDrawcall = &m_Drawcalls->at(0); while(!m_FirstDrawcall->children.empty()) m_FirstDrawcall = &m_FirstDrawcall->children[0]; - m_LastDrawcall = &m_Drawcalls.back(); + m_LastDrawcall = &m_Drawcalls->back(); while(!m_LastDrawcall->children.empty()) m_LastDrawcall = &m_LastDrawcall->children.back(); @@ -401,220 +404,6 @@ void CaptureContext::CacheResources() m_Resources[res.resourceId] = &res; } -bool CaptureContext::PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b) -{ - // executing command lists can have children - if(!a.children.empty() || !b.children.empty()) - return false; - - // don't group draws and compute executes - if((a.flags & DrawFlags::Dispatch) != (b.flags & DrawFlags::Dispatch)) - return false; - - // don't group present with anything - if((a.flags & DrawFlags::Present) != (b.flags & DrawFlags::Present)) - return false; - - // don't group things with different depth outputs - if(a.depthOut != b.depthOut) - return false; - - int numAOuts = 0, numBOuts = 0; - for(int i = 0; i < 8; i++) - { - if(a.outputs[i] != ResourceId()) - numAOuts++; - if(b.outputs[i] != ResourceId()) - numBOuts++; - } - - int numSame = 0; - - if(a.depthOut != ResourceId()) - { - numAOuts++; - numBOuts++; - numSame++; - } - - for(int i = 0; i < 8; i++) - { - if(a.outputs[i] != ResourceId()) - { - for(int j = 0; j < 8; j++) - { - if(a.outputs[i] == b.outputs[j]) - { - numSame++; - break; - } - } - } - else if(b.outputs[i] != ResourceId()) - { - for(int j = 0; j < 8; j++) - { - if(a.outputs[j] == b.outputs[i]) - { - numSame++; - break; - } - } - } - } - - // use a kind of heuristic to group together passes where the outputs are similar enough. - // could be useful for example if you're rendering to a gbuffer and sometimes you render - // without one target, but the draws are still batched up. - if(numSame > qMax(numAOuts, numBOuts) / 2 && qMax(numAOuts, numBOuts) > 1) - return true; - - if(numSame == qMax(numAOuts, numBOuts)) - return true; - - return false; -} - -bool CaptureContext::ContainsMarker(const rdcarray &draws) -{ - bool ret = false; - - for(const DrawcallDescription &d : draws) - { - ret |= (d.flags & DrawFlags::PushMarker) && - !(d.flags & (DrawFlags::CmdList | DrawFlags::MultiDraw)) && !d.children.empty(); - ret |= ContainsMarker(d.children); - - if(ret) - break; - } - - return ret; -} - -void CaptureContext::AddFakeProfileMarkers() -{ - rdcarray &draws = m_Drawcalls; - - if(!Config().EventBrowser_AddFake) - return; - - if(ContainsMarker(draws)) - return; - - QList ret; - - int depthpassID = 1; - int copypassID = 1; - int computepassID = 1; - int passID = 1; - - int start = 0; - int refdraw = 0; - - DrawFlags drawFlags = DrawFlags::Copy | DrawFlags::Resolve | DrawFlags::SetMarker | - DrawFlags::APICalls | DrawFlags::CmdList; - - for(int32_t i = 1; i < draws.count(); i++) - { - if(draws[refdraw].flags & drawFlags) - { - refdraw = i; - continue; - } - - if(draws[i].flags & drawFlags) - continue; - - if(PassEquivalent(draws[i], draws[refdraw])) - continue; - - int end = i - 1; - - if(end - start < 2 || !draws[i].children.empty() || !draws[refdraw].children.empty()) - { - for(int j = start; j <= end; j++) - ret.push_back(draws[j]); - - start = i; - refdraw = i; - continue; - } - - int minOutCount = 100; - int maxOutCount = 0; - bool copyOnly = true; - - for(int j = start; j <= end; j++) - { - int outCount = 0; - - if(!(draws[j].flags & (DrawFlags::Copy | DrawFlags::Resolve | DrawFlags::Clear))) - copyOnly = false; - - for(ResourceId o : draws[j].outputs) - if(o != ResourceId()) - outCount++; - minOutCount = qMin(minOutCount, outCount); - maxOutCount = qMax(maxOutCount, outCount); - } - - DrawcallDescription mark; - - mark.eventId = draws[start].eventId; - mark.drawcallId = draws[start].drawcallId; - - mark.flags = DrawFlags::PushMarker; - memcpy(mark.outputs, draws[end].outputs, sizeof(mark.outputs)); - mark.depthOut = draws[end].depthOut; - - mark.name = "Guessed Pass"; - - minOutCount = qMax(1, minOutCount); - - QString targets = draws[end].depthOut == ResourceId() ? tr("Targets") : tr("Targets + Depth"); - - if(copyOnly) - mark.name = tr("Copy/Clear Pass #%1").arg(copypassID++).toUtf8().data(); - else if(draws[refdraw].flags & DrawFlags::Dispatch) - mark.name = tr("Compute Pass #%1").arg(computepassID++).toUtf8().data(); - else if(maxOutCount == 0) - mark.name = tr("Depth-only Pass #%1").arg(depthpassID++).toUtf8().data(); - else if(minOutCount == maxOutCount) - mark.name = - tr("Colour Pass #%1 (%2 %3)").arg(passID++).arg(minOutCount).arg(targets).toUtf8().data(); - else - mark.name = tr("Colour Pass #%1 (%2-%3 %4)") - .arg(passID++) - .arg(minOutCount) - .arg(maxOutCount) - .arg(targets) - .toUtf8() - .data(); - - mark.children.resize(end - start + 1); - - for(int j = start; j <= end; j++) - { - mark.children[j - start] = draws[j]; - draws[j].parent = mark.eventId; - } - - ret.push_back(mark); - - start = i; - refdraw = i; - } - - if(start < draws.count()) - { - for(int j = start; j < draws.count(); j++) - ret.push_back(draws[j]); - } - - m_Drawcalls = ret; -} - void CaptureContext::RecompressCapture() { QString destFilename = GetCaptureFilename(); @@ -857,7 +646,7 @@ void CaptureContext::CloseCapture() m_Bookmarks.clear(); m_Notes.clear(); - m_Drawcalls.clear(); + m_Drawcalls = &m_EmptyDraws; m_FirstDrawcall = m_LastDrawcall = NULL; m_CurD3D11PipelineState = NULL; diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 44ab4f85a..ae62692c1 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -118,7 +118,7 @@ public: const DrawcallDescription *GetLastDrawcall() override { return m_LastDrawcall; }; bool OpenRGPProfile(const rdcstr &filename) override; IRGPInterop *GetRGPInterop() override { return m_RGP; } - const rdcarray &CurDrawcalls() override { return m_Drawcalls; } + const rdcarray &CurDrawcalls() override { return *m_Drawcalls; } ResourceDescription *GetResource(ResourceId id) override { return m_Resources[id]; } const rdcarray &GetResources() override { return m_ResourceList; } rdcstr GetResourceName(ResourceId id) override; @@ -132,7 +132,7 @@ public: const rdcarray &GetBuffers() override { return m_BufferList; } const DrawcallDescription *GetDrawcall(uint32_t eventId) override { - return GetDrawcall(m_Drawcalls, eventId); + return GetDrawcall(*m_Drawcalls, eventId); } const SDFile &GetStructuredFile() override { return *m_StructuredFile; } WindowingSystem CurWindowingSystem() override { return m_CurWinSystem; } @@ -246,10 +246,6 @@ private: rdcarray m_DebugMessages; int m_UnreadMessageCount = 0; - bool PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b); - bool ContainsMarker(const rdcarray &m_Drawcalls); - void AddFakeProfileMarkers(); - void SaveChanges(); bool SaveRenames(); @@ -293,12 +289,13 @@ private: } void setupDockWindow(QWidget *shad); - rdcarray m_Drawcalls; + const rdcarray *m_Drawcalls; + rdcarray m_EmptyDraws; APIProperties m_APIProps; FrameDescription m_FrameInfo; - DrawcallDescription *m_FirstDrawcall = NULL; - DrawcallDescription *m_LastDrawcall = NULL; + const DrawcallDescription *m_FirstDrawcall = NULL; + const DrawcallDescription *m_LastDrawcall = NULL; IRGPInterop *m_RGP = NULL; diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 973ee25ba..7f3d45b2e 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -627,13 +627,13 @@ void CombineUsageEvents(ICaptureContext &ctx, const rdcarray &usage, // last event was where we were - otherwise it's a new // distinct set of drawcalls and should have a separate // entry in the context menu - const DrawcallDescription *prev = ctx.GetDrawcall(draw->previous); + const DrawcallDescription *prev = draw->previous; while(prev != NULL && prev->eventId > end) { if(!(prev->flags & (DrawFlags::Dispatch | DrawFlags::Drawcall | DrawFlags::CmdList))) { - prev = ctx.GetDrawcall(prev->previous); + prev = prev->previous; } else { diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 710ac7149..a1f6e1259 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -573,8 +573,8 @@ void EventBrowser::on_stepNext_clicked() const DrawcallDescription *draw = m_Ctx.CurDrawcall(); - if(draw && draw->next > 0) - SelectEvent(draw->next); + if(draw && draw->next) + SelectEvent(draw->next->eventId); // special case for the first 'virtual' draw at EID 0 if(m_Ctx.CurEvent() == 0) @@ -588,8 +588,8 @@ void EventBrowser::on_stepPrev_clicked() const DrawcallDescription *draw = m_Ctx.CurDrawcall(); - if(draw && draw->previous > 0) - SelectEvent(draw->previous); + if(draw && draw->previous) + SelectEvent(draw->previous->eventId); // special case for the first 'virtual' draw at EID 0 if(m_Ctx.CurEvent() == m_Ctx.GetFirstDrawcall()->eventId) diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index a4fd58011..ac5f36a05 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -291,11 +291,11 @@ div.stage table tr td { border-right: 1px solid #AAAAAA; background-color: #EEEE const DrawcallDescription *draw = m_Ctx.CurDrawcall(); QList drawstack; - const DrawcallDescription *parent = m_Ctx.GetDrawcall(draw->parent); + const DrawcallDescription *parent = draw->parent; while(parent) { drawstack.push_front(parent); - parent = m_Ctx.GetDrawcall(parent->parent); + parent = parent->parent; } for(const DrawcallDescription *d : drawstack) diff --git a/qrenderdoc/Windows/PixelHistoryView.cpp b/qrenderdoc/Windows/PixelHistoryView.cpp index d0c13bc77..bcfc5d24c 100644 --- a/qrenderdoc/Windows/PixelHistoryView.cpp +++ b/qrenderdoc/Windows/PixelHistoryView.cpp @@ -199,11 +199,11 @@ public: QString ret; QList drawstack; - const DrawcallDescription *parent = m_Ctx.GetDrawcall(drawcall->parent); + const DrawcallDescription *parent = drawcall->parent; while(parent) { drawstack.push_back(parent); - parent = m_Ctx.GetDrawcall(parent->parent); + parent = parent->parent; } if(!drawstack.isEmpty()) diff --git a/renderdoc/api/replay/data_types.h b/renderdoc/api/replay/data_types.h index 971766030..945bdc8c6 100644 --- a/renderdoc/api/replay/data_types.h +++ b/renderdoc/api/replay/data_types.h @@ -963,9 +963,7 @@ struct DrawcallDescription copySource = ResourceId(); copyDestination = ResourceId(); - parent = 0; - previous = 0; - next = 0; + parent = previous = next = NULL; for(int i = 0; i < 8; i++) outputs[i] = ResourceId(); @@ -1044,19 +1042,17 @@ operation. )"); ResourceId copyDestination; - DOCUMENT(R"(The :data:`eventId ` of the parent of this drawcall, or ``0`` if there -is no parent for this drawcall. + DOCUMENT(R"(The parent of this drawcall, or ``None`` if there is no parent for this drawcall. )"); - int64_t parent; + const DrawcallDescription *parent; - DOCUMENT(R"(The :data:`eventId ` of the previous drawcall in the frame, or ``0`` if -this is the first drawcall in the frame. + DOCUMENT(R"(The previous drawcall in the frame, or ``None`` if this is the first drawcall in the +frame. )"); - int64_t previous; - DOCUMENT(R"(The :data:`eventId ` of the next drawcall in the frame, or ``0`` if this -is the last drawcall in the frame. -)"); - int64_t next; + const DrawcallDescription *previous; + DOCUMENT( + "The next drawcall in the frame, or ``None`` if this is the last drawcall in the frame."); + const DrawcallDescription *next; DOCUMENT(R"(A simple list of the :class:`ResourceId` ids for the color outputs, which can be used for very coarse bucketing of drawcalls into similar passes by their outputs. diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 3ec1ec07a..b05eead53 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -939,7 +939,14 @@ See :meth:`BuildTargetShader`. :return: The list of root-level drawcalls in the capture. :rtype: ``list`` of :class:`DrawcallDescription` )"); - virtual rdcarray GetDrawcalls() = 0; + virtual void AddFakeMarkers() = 0; + + DOCUMENT(R"(Retrieve the list of root-level drawcalls in the capture. + +:return: The list of root-level drawcalls in the capture. +:rtype: ``list`` of :class:`DrawcallDescription` +)"); + virtual const rdcarray &GetDrawcalls() = 0; DOCUMENT(R"(Retrieve the values of a specified set of counters. diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 080c4ba9a..e0847f774 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -381,6 +381,13 @@ FrameRecord ReplayProxy::Proxied_GetFrameRecord(ParamSerialiser ¶mser, Retur SERIALISE_RETURN(ret); + if(paramser.IsWriting()) + { + // re-configure the drawcall pointers, since they will be invalid + DrawcallDescription *previous = NULL; + SetupDrawcallPointers(m_Drawcalls, ret.drawcallList, NULL, previous); + } + return ret; } diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index c015a03a6..123053605 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -649,6 +649,8 @@ private: FrameRecord m_FrameRecord; APIProperties m_APIProps; + std::vector m_Drawcalls; + SDFile m_StructuredFile; std::vector m_Resources; diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 5c8d4bb73..820500049 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -1083,7 +1083,7 @@ ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool store if(!IsStructuredExporting(m_State)) { DrawcallDescription *previous = NULL; - SetupDrawcallPointers(&m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); + SetupDrawcallPointers(m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); } #if ENABLED(RDOC_DEVEL) diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 978ee9c50..64b2b4c5e 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -1456,10 +1456,9 @@ vector D3D11Replay::GetPassEvents(uint32_t eventId) const DrawcallDescription *draw = m_pDevice->GetDrawcall(eventId); const DrawcallDescription *start = draw; - while(start && start->previous != 0 && - !(m_pDevice->GetDrawcall((uint32_t)start->previous)->flags & DrawFlags::Clear)) + while(start && start->previous && !(start->previous->flags & DrawFlags::Clear)) { - const DrawcallDescription *prev = m_pDevice->GetDrawcall((uint32_t)start->previous); + const DrawcallDescription *prev = start->previous; if(memcmp(start->outputs, prev->outputs, sizeof(start->outputs)) || start->depthOut != prev->depthOut) @@ -1476,7 +1475,7 @@ vector D3D11Replay::GetPassEvents(uint32_t eventId) if(start->flags & DrawFlags::Drawcall) passEvents.push_back(start->eventId); - start = m_pDevice->GetDrawcall((uint32_t)start->next); + start = start->next; } return passEvents; diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 645cab91a..37fea8946 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -2721,7 +2721,7 @@ ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool store m_Queue->GetParentDrawcall().children.clear(); DrawcallDescription *previous = NULL; - SetupDrawcallPointers(&m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); + SetupDrawcallPointers(m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); D3D12CommandData &cmd = *m_Queue->GetCommandData(); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 59928626b..836cf96b8 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -2528,7 +2528,7 @@ vector D3D12Replay::GetPassEvents(uint32_t eventId) return passEvents; // step back - const DrawcallDescription *prev = m_pDevice->GetDrawcall((uint32_t)start->previous); + const DrawcallDescription *prev = start->previous; // something went wrong, start->previous was non-zero but we didn't // get a draw. Abort @@ -2556,7 +2556,7 @@ vector D3D12Replay::GetPassEvents(uint32_t eventId) if(start->flags & (DrawFlags::Drawcall | DrawFlags::PassBoundary)) passEvents.push_back(start->eventId); - start = m_pDevice->GetDrawcall((uint32_t)start->next); + start = start->next; } return passEvents; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 4dddb08c2..3f1812d4a 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -4374,7 +4374,7 @@ ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t sta GetFrameRecord().frameInfo.debugMessages = GetDebugMessages(); DrawcallDescription *previous = NULL; - SetupDrawcallPointers(&m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); + SetupDrawcallPointers(m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); // it's easier to remove duplicate usages here than check it as we go. // this means if textures are bound in multiple places in the same draw diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index d5632e4b4..90f8f602c 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -98,10 +98,9 @@ vector GLReplay::GetPassEvents(uint32_t eventId) const DrawcallDescription *draw = m_pDriver->GetDrawcall(eventId); const DrawcallDescription *start = draw; - while(start && start->previous != 0 && - !(m_pDriver->GetDrawcall((uint32_t)start->previous)->flags & DrawFlags::Clear)) + while(start && start->previous && !(start->previous->flags & DrawFlags::Clear)) { - const DrawcallDescription *prev = m_pDriver->GetDrawcall((uint32_t)start->previous); + const DrawcallDescription *prev = start->previous; if(memcmp(start->outputs, prev->outputs, sizeof(start->outputs)) || start->depthOut != prev->depthOut) @@ -118,7 +117,7 @@ vector GLReplay::GetPassEvents(uint32_t eventId) if(start->flags & DrawFlags::Drawcall) passEvents.push_back(start->eventId); - start = m_pDriver->GetDrawcall((uint32_t)start->next); + start = start->next; } return passEvents; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 2340c8b96..cedaf267e 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1985,7 +1985,7 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta GetFrameRecord().drawcallList = m_ParentDrawcall.Bake(); DrawcallDescription *previous = NULL; - SetupDrawcallPointers(&m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); + SetupDrawcallPointers(m_Drawcalls, GetFrameRecord().drawcallList, NULL, previous); struct SortEID { diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index be7a2c29c..15c10e729 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -130,11 +130,11 @@ vector VulkanReplay::GetPassEvents(uint32_t eventId) // if we've come to the start of the log we were outside of a render pass // to start with - if(start->previous == 0) + if(start->previous == NULL) return passEvents; // step back - start = m_pDriver->GetDrawcall((uint32_t)start->previous); + start = start->previous; // something went wrong, start->previous was non-zero but we didn't // get a draw. Abort @@ -155,7 +155,7 @@ vector VulkanReplay::GetPassEvents(uint32_t eventId) if(start->flags & (DrawFlags::Drawcall | DrawFlags::PassBoundary)) passEvents.push_back(start->eventId); - start = m_pDriver->GetDrawcall((uint32_t)start->next); + start = start->next; } return passEvents; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 94f92172a..6c91190ae 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -491,9 +491,8 @@ void DoSerialise(SerialiserType &ser, DrawcallDescription &el) SERIALISE_MEMBER(copySource); SERIALISE_MEMBER(copyDestination); - SERIALISE_MEMBER(parent); - SERIALISE_MEMBER(previous); - SERIALISE_MEMBER(next); + if(ser.IsReading()) + el.parent = el.previous = el.next = NULL; SERIALISE_MEMBER(outputs); SERIALISE_MEMBER(depthOut); diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 61be3f6c7..fa514f35d 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -290,11 +290,218 @@ DrawcallDescription *ReplayController::GetDrawcallByEID(uint32_t eventId) return m_Drawcalls[eventId]; } -rdcarray ReplayController::GetDrawcalls() +const rdcarray &ReplayController::GetDrawcalls() { return m_FrameRecord.drawcallList; } +bool ReplayController::ContainsMarker(const rdcarray &draws) +{ + bool ret = false; + + for(const DrawcallDescription &d : draws) + { + ret |= (d.flags & DrawFlags::PushMarker) && + !(d.flags & (DrawFlags::CmdList | DrawFlags::MultiDraw)) && !d.children.empty(); + ret |= ContainsMarker(d.children); + + if(ret) + break; + } + + return ret; +} + +bool ReplayController::PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b) +{ + // executing command lists can have children + if(!a.children.empty() || !b.children.empty()) + return false; + + // don't group draws and compute executes + if((a.flags & DrawFlags::Dispatch) != (b.flags & DrawFlags::Dispatch)) + return false; + + // don't group present with anything + if((a.flags & DrawFlags::Present) != (b.flags & DrawFlags::Present)) + return false; + + // don't group things with different depth outputs + if(a.depthOut != b.depthOut) + return false; + + int numAOuts = 0, numBOuts = 0; + for(int i = 0; i < 8; i++) + { + if(a.outputs[i] != ResourceId()) + numAOuts++; + if(b.outputs[i] != ResourceId()) + numBOuts++; + } + + int numSame = 0; + + if(a.depthOut != ResourceId()) + { + numAOuts++; + numBOuts++; + numSame++; + } + + for(int i = 0; i < 8; i++) + { + if(a.outputs[i] != ResourceId()) + { + for(int j = 0; j < 8; j++) + { + if(a.outputs[i] == b.outputs[j]) + { + numSame++; + break; + } + } + } + else if(b.outputs[i] != ResourceId()) + { + for(int j = 0; j < 8; j++) + { + if(a.outputs[j] == b.outputs[i]) + { + numSame++; + break; + } + } + } + } + + // use a kind of heuristic to group together passes where the outputs are similar enough. + // could be useful for example if you're rendering to a gbuffer and sometimes you render + // without one target, but the draws are still batched up. + if(numSame > RDCMAX(numAOuts, numBOuts) / 2 && RDCMAX(numAOuts, numBOuts) > 1) + return true; + + if(numSame == RDCMAX(numAOuts, numBOuts)) + return true; + + return false; +} + +void ReplayController::AddFakeMarkers() +{ + rdcarray &draws = m_FrameRecord.drawcallList; + + if(ContainsMarker(draws)) + return; + + std::vector ret; + + int depthpassID = 1; + int copypassID = 1; + int computepassID = 1; + int passID = 1; + + int start = 0; + int refdraw = 0; + + DrawFlags drawFlags = DrawFlags::Copy | DrawFlags::Resolve | DrawFlags::SetMarker | + DrawFlags::APICalls | DrawFlags::CmdList; + + for(int32_t i = 1; i < draws.count(); i++) + { + if(draws[refdraw].flags & drawFlags) + { + refdraw = i; + continue; + } + + if(draws[i].flags & drawFlags) + continue; + + if(PassEquivalent(draws[i], draws[refdraw])) + continue; + + int end = i - 1; + + if(end - start < 2 || !draws[i].children.empty() || !draws[refdraw].children.empty()) + { + for(int j = start; j <= end; j++) + ret.push_back(draws[j]); + + start = i; + refdraw = i; + continue; + } + + int minOutCount = 100; + int maxOutCount = 0; + bool copyOnly = true; + + for(int j = start; j <= end; j++) + { + int outCount = 0; + + if(!(draws[j].flags & (DrawFlags::Copy | DrawFlags::Resolve | DrawFlags::Clear))) + copyOnly = false; + + for(ResourceId o : draws[j].outputs) + if(o != ResourceId()) + outCount++; + minOutCount = RDCMIN(minOutCount, outCount); + maxOutCount = RDCMAX(maxOutCount, outCount); + } + + DrawcallDescription mark; + + mark.eventId = draws[start].eventId; + mark.drawcallId = draws[start].drawcallId; + + mark.flags = DrawFlags::PushMarker; + memcpy(mark.outputs, draws[end].outputs, sizeof(mark.outputs)); + mark.depthOut = draws[end].depthOut; + + mark.name = "Guessed Pass"; + + minOutCount = RDCMAX(1, minOutCount); + + const char *targets = draws[end].depthOut == ResourceId() ? "Targets" : "Targets + Depth"; + + if(copyOnly) + mark.name = StringFormat::Fmt("Copy/Clear Pass #%d", copypassID++); + else if(draws[refdraw].flags & DrawFlags::Dispatch) + mark.name = StringFormat::Fmt("Compute Pass #%d", computepassID++); + else if(maxOutCount == 0) + mark.name = StringFormat::Fmt("Depth-only Pass #%d", depthpassID++); + else if(minOutCount == maxOutCount) + mark.name = StringFormat::Fmt("Colour Pass #%d (%d %s)", passID++, minOutCount, targets); + else + mark.name = StringFormat::Fmt("Colour Pass #%d (%d-%d %s)", passID++, minOutCount, + maxOutCount, targets); + + mark.children.resize(end - start + 1); + + for(int j = start; j <= end; j++) + mark.children[j - start] = draws[j]; + + ret.push_back(mark); + + start = i; + refdraw = i; + } + + if(start < draws.count()) + { + for(int j = start; j < draws.count(); j++) + ret.push_back(draws[j]); + } + + m_FrameRecord.drawcallList = ret; + + // re-configure the previous/next pointeres + DrawcallDescription *previous = NULL; + m_Drawcalls.clear(); + SetupDrawcallPointers(m_Drawcalls, m_FrameRecord.drawcallList, NULL, previous); +} + rdcarray ReplayController::FetchCounters(const rdcarray &counters) { std::vector counterArray(counters.begin(), counters.end()); @@ -1718,7 +1925,8 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd return ReplayStatus::APIReplayFailed; DrawcallDescription *previous = NULL; - SetupDrawcallPointers(&m_Drawcalls, m_FrameRecord.drawcallList, NULL, previous); + m_Drawcalls.clear(); + SetupDrawcallPointers(m_Drawcalls, m_FrameRecord.drawcallList, NULL, previous); return ReplayStatus::Succeeded; } diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 948e13bfb..24db80a20 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -159,7 +159,8 @@ public: FrameDescription GetFrameInfo(); const SDFile &GetStructuredFile(); - rdcarray GetDrawcalls(); + const rdcarray &GetDrawcalls(); + void AddFakeMarkers(); rdcarray FetchCounters(const rdcarray &counters); rdcarray EnumerateCounters(); CounterDescription DescribeCounter(GPUCounter counterID); @@ -208,6 +209,8 @@ private: ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc); DrawcallDescription *GetDrawcallByEID(uint32_t eventId); + bool ContainsMarker(const rdcarray &draws); + bool PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b); IReplayDriver *GetDevice() { return m_pDevice; } FrameRecord m_FrameRecord; diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index 02e3336e7..e95fd4e01 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -53,7 +53,7 @@ void DoSerialise(SerialiserType &ser, GetTextureDataParams &el) INSTANTIATE_SERIALISE_TYPE(GetTextureDataParams); -DrawcallDescription *SetupDrawcallPointers(vector *drawcallTable, +DrawcallDescription *SetupDrawcallPointers(vector &drawcallTable, rdcarray &draws, DrawcallDescription *parent, DrawcallDescription *&previous) @@ -64,15 +64,14 @@ DrawcallDescription *SetupDrawcallPointers(vector *drawca { DrawcallDescription *draw = &draws[i]; - draw->parent = parent ? parent->eventId : 0; + draw->parent = parent; if(!draw->children.empty()) { - if(drawcallTable) { - RDCASSERT(drawcallTable->empty() || draw->eventId > drawcallTable->back()->eventId); - drawcallTable->resize(RDCMAX(drawcallTable->size(), size_t(draw->eventId + 1))); - (*drawcallTable)[draw->eventId] = draw; + RDCASSERT(drawcallTable.empty() || draw->eventId > drawcallTable.back()->eventId); + drawcallTable.resize(RDCMAX(drawcallTable.size(), size_t(draw->eventId + 1))); + drawcallTable[draw->eventId] = draw; } ret = SetupDrawcallPointers(drawcallTable, draw->children, draw, previous); @@ -82,24 +81,25 @@ DrawcallDescription *SetupDrawcallPointers(vector *drawca // don't want to set up previous/next links for markers, but still add them to the table // Some markers like Present or API Calls should have previous/next and are not markers - if(drawcallTable) { - RDCASSERT(drawcallTable->empty() || draw->eventId > drawcallTable->back()->eventId); - drawcallTable->resize(RDCMAX(drawcallTable->size(), size_t(draw->eventId + 1))); - (*drawcallTable)[draw->eventId] = draw; + RDCASSERT(drawcallTable.empty() || draw->eventId > drawcallTable.back()->eventId); + drawcallTable.resize(RDCMAX(drawcallTable.size(), size_t(draw->eventId + 1))); + drawcallTable[draw->eventId] = draw; } } else { - if(previous != NULL) - previous->next = draw->eventId; - draw->previous = previous ? previous->eventId : 0; + if(previous) + previous->next = draw; + draw->previous = previous; - if(drawcallTable) { - RDCASSERT(drawcallTable->empty() || draw->eventId > drawcallTable->back()->eventId); - drawcallTable->resize(RDCMAX(drawcallTable->size(), size_t(draw->eventId + 1))); - (*drawcallTable)[draw->eventId] = draw; + // we also allow equal EIDs for fake markers that don't have their own EIDs + RDCASSERT(drawcallTable.empty() || draw->eventId > drawcallTable.back()->eventId || + (draw->eventId == drawcallTable.back()->eventId && + (drawcallTable.back()->flags & DrawFlags::PushMarker))); + drawcallTable.resize(RDCMAX(drawcallTable.size(), size_t(draw->eventId + 1))); + drawcallTable[draw->eventId] = draw; } ret = previous = draw; diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 5abc83dba..a351a5b66 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -223,7 +223,7 @@ public: }; // utility functions useful in any driver implementation -DrawcallDescription *SetupDrawcallPointers(std::vector *drawcallTable, +DrawcallDescription *SetupDrawcallPointers(std::vector &drawcallTable, rdcarray &draws, DrawcallDescription *parent, DrawcallDescription *&previous);