diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 2e53cca06..8beb0df3d 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -33,6 +33,7 @@ #include #include #include +#include "Windows/APIInspector.h" #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/LiveCapture.h" #include "Windows/EventBrowser.h" @@ -429,6 +430,18 @@ EventBrowser *CaptureContext::eventBrowser() return m_EventBrowser; } +APIInspector *CaptureContext::apiInspector() +{ + if(m_APIInspector) + return m_APIInspector; + + m_APIInspector = new APIInspector(this, m_MainWindow); + m_APIInspector->setObjectName("apiInspector"); + m_APIInspector->setWindowIcon(*m_Icon); + + return m_APIInspector; +} + TextureViewer *CaptureContext::textureViewer() { if(m_TextureViewer) @@ -471,6 +484,10 @@ QWidget *CaptureContext::createToolWindow(const QString &objectName) { return eventBrowser(); } + else if(objectName == "apiInspector") + { + return apiInspector(); + } else if(objectName == "capDialog") { return captureDialog(); diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index fd1b35da8..3cce6388b 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -47,6 +47,7 @@ struct ILogViewerForm class MainWindow; class EventBrowser; +class APIInspector; class TextureViewer; class CaptureDialog; class QProgressDialog; @@ -126,10 +127,12 @@ public: const QIcon &winIcon() { return *m_Icon; } MainWindow *mainWindow() { return m_MainWindow; } EventBrowser *eventBrowser(); + APIInspector *apiInspector(); TextureViewer *textureViewer(); CaptureDialog *captureDialog(); bool hasEventBrowser() { return m_EventBrowser != NULL; } + bool hasAPIInspector() { return m_APIInspector != NULL; } bool hasTextureViewer() { return m_TextureViewer != NULL; } bool hasCaptureDialog() { return m_CaptureDialog != NULL; } QWidget *createToolWindow(const QString &objectName); @@ -197,6 +200,7 @@ private: QProgressDialog *m_Progress; MainWindow *m_MainWindow = NULL; EventBrowser *m_EventBrowser = NULL; + APIInspector *m_APIInspector = NULL; TextureViewer *m_TextureViewer = NULL; CaptureDialog *m_CaptureDialog = NULL; }; diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp new file mode 100644 index 000000000..1993ab2fb --- /dev/null +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -0,0 +1,166 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2016 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "APIInspector.h" +#include +#include "ui_APIInspector.h" + +Q_DECLARE_METATYPE(FetchAPIEvent); + +APIInspector::APIInspector(CaptureContext *ctx, QWidget *parent) + : QFrame(parent), ui(new Ui::APIInspector), m_Ctx(ctx) +{ + ui->setupUi(this); + + ui->apiEvents->headerItem()->setText(0, "EID"); + ui->apiEvents->headerItem()->setText(1, "Event"); + + ui->splitter->setCollapsible(1, true); + ui->splitter->setSizes({1, 0}); + + m_Ctx->AddLogViewer(this); +} + +APIInspector::~APIInspector() +{ + m_Ctx->windowClosed(this); + m_Ctx->RemoveLogViewer(this); + delete ui; +} + +void APIInspector::OnLogfileLoaded() +{ +} + +void APIInspector::OnLogfileClosed() +{ + ui->apiEvents->clear(); + ui->callstack->clear(); +} + +void APIInspector::OnEventSelected(uint32_t eventID) +{ + ui->apiEvents->clearSelection(); + + fillAPIView(); +} + +void APIInspector::addCallstack(rdctype::array calls) +{ + ui->callstack->setUpdatesEnabled(false); + ui->callstack->clear(); + + if(calls.count == 1 && calls[0].count == 0) + { + ui->callstack->addItem(tr("Symbols not loaded. Tools -> Resolve Symbols.")); + } + else + { + for(rdctype::str &s : calls) + ui->callstack->addItem(ToQStr(s)); + } + ui->callstack->setUpdatesEnabled(true); +} + +void APIInspector::on_apiEvents_itemSelectionChanged() +{ + if(ui->apiEvents->selectedItems().count() == 0) + return; + + FetchAPIEvent ev = ui->apiEvents->selectedItems()[0]->data(0, Qt::UserRole).value(); + + if(ev.callstack.count > 0) + { + m_Ctx->Renderer()->AsyncInvoke([this, ev](IReplayRenderer *r) { + rdctype::array trace; + r->GetResolve(ev.callstack.elems, ev.callstack.count, &trace); + + GUIInvoke::call([this, trace]() { addCallstack(trace); }); + }); + } + else + { + ui->callstack->setUpdatesEnabled(false); + ui->callstack->clear(); + ui->callstack->addItem(tr("No Callstack available.")); + ui->callstack->setUpdatesEnabled(true); + } +} + +void APIInspector::fillAPIView() +{ + ui->apiEvents->setUpdatesEnabled(false); + ui->apiEvents->clear(); + + QRegularExpression rgxopen("^\\s*{"); + QRegularExpression rgxclose("^\\s*}"); + + const FetchDrawcall *draw = m_Ctx->CurDrawcall(); + + if(draw != NULL && draw->events.count > 0) + { + int e = 0; + for(const FetchAPIEvent &ev : draw->events) + { + QStringList lines = ToQStr(ev.eventDesc).split("\n", QString::SkipEmptyParts); + + QTreeWidgetItem *root = + new QTreeWidgetItem(ui->apiEvents, QStringList{QString::number(ev.eventID), lines[0]}); + + int i = 1; + + if(i < lines.count() && lines[i].trimmed() == "{") + i++; + + QList nodestack; + nodestack.push_back(root); + + for(; i < lines.count(); i++) + { + if(rgxopen.match(lines[i]).hasMatch()) + nodestack.push_back(nodestack.back()->child(nodestack.back()->childCount() - 1)); + else if(rgxclose.match(lines[i]).hasMatch()) + nodestack.pop_back(); + else if(!nodestack.empty()) + new QTreeWidgetItem(nodestack.back(), QStringList{"", lines[i].trimmed()}); + } + + if(ev.eventID == draw->eventID) + { + QFont font = root->font(1); + font.setBold(true); + root->setFont(1, font); + } + + root->setData(0, Qt::UserRole, QVariant::fromValue(ev)); + + ui->apiEvents->insertTopLevelItem(e, root); + e++; + + ui->apiEvents->clearSelection(); + ui->apiEvents->setItemSelected(root, true); + } + } + ui->apiEvents->setUpdatesEnabled(true); +} \ No newline at end of file diff --git a/qrenderdoc/Windows/APIInspector.h b/qrenderdoc/Windows/APIInspector.h new file mode 100644 index 000000000..62c65af20 --- /dev/null +++ b/qrenderdoc/Windows/APIInspector.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2016 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include +#include "Code/CaptureContext.h" + +namespace Ui +{ +class APIInspector; +} + +class APIInspector : public QFrame, public ILogViewerForm +{ + Q_OBJECT + +public: + explicit APIInspector(CaptureContext *ctx, QWidget *parent = 0); + ~APIInspector(); + + void OnLogfileLoaded(); + void OnLogfileClosed(); + void OnEventSelected(uint32_t eventID); + +public slots: + void on_apiEvents_itemSelectionChanged(); + +private: + Ui::APIInspector *ui; + CaptureContext *m_Ctx = NULL; + + void addCallstack(rdctype::array calls); + void fillAPIView(); +}; diff --git a/qrenderdoc/Windows/APIInspector.ui b/qrenderdoc/Windows/APIInspector.ui new file mode 100644 index 000000000..d1f2eeefa --- /dev/null +++ b/qrenderdoc/Windows/APIInspector.ui @@ -0,0 +1,90 @@ + + + APIInspector + + + + 0 + 0 + 434 + 425 + + + + API Inspector + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + 16 + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + 20 + + + true + + + true + + + true + + + 2 + + + + 1 + + + + + 2 + + + + + + + 0 + 1 + + + + + + + + + + diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index e6f31f571..f863cd4b6 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -27,12 +27,14 @@ #include #include #include +#include #include "Code/CaptureContext.h" #include "Code/QRDUtils.h" #include "Resources/resource.h" #include "Windows/Dialogs/AboutDialog.h" #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/LiveCapture.h" +#include "APIInspector.h" #include "EventBrowser.h" #include "TextureViewer.h" #include "ui_MainWindow.h" @@ -126,6 +128,9 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main return m_Ctx->createToolWindow(objectName); }); + ui->action_Resolve_Symbols->setEnabled(false); + ui->action_Resolve_Symbols->setText(tr("Resolve Symbols")); + bool loaded = LoadLayout(0); // create default layout if layout failed to load @@ -147,6 +152,13 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main ui->toolWindowManager->addToolWindow( capDialog, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->toolWindowManager->areaOf(textureViewer))); + + APIInspector *apiInspector = m_Ctx->apiInspector(); + + ui->toolWindowManager->addToolWindow( + apiInspector, + ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, + ui->toolWindowManager->areaOf(eventBrowser), 0.3f)); } m_Ctx->AddLogViewer(this); @@ -917,6 +929,29 @@ void MainWindow::on_action_Mesh_Output_triggered() // TODO Mesh view } +void MainWindow::on_action_API_Inspector_triggered() +{ + APIInspector *apiInspector = m_Ctx->apiInspector(); + + if(ui->toolWindowManager->toolWindows().contains(apiInspector)) + { + ToolWindowManager::raiseToolWindow(apiInspector); + } + else + { + if(m_Ctx->hasEventBrowser()) + { + ToolWindowManager::AreaReference ref(ToolWindowManager::BottomOf, + ui->toolWindowManager->areaOf(m_Ctx->eventBrowser())); + ui->toolWindowManager->addToolWindow(apiInspector, ref); + } + else + { + ui->toolWindowManager->addToolWindow(apiInspector, leftToolArea()); + } + } +} + void MainWindow::on_action_Event_Browser_triggered() { EventBrowser *eventBrowser = m_Ctx->eventBrowser(); @@ -961,6 +996,51 @@ void MainWindow::on_action_Inject_into_Process_triggered() ui->toolWindowManager->addToolWindow(capDialog, mainToolArea()); } +void MainWindow::on_action_Resolve_Symbols_triggered() +{ + m_Ctx->Renderer()->AsyncInvoke([this](IReplayRenderer *r) { r->InitResolver(); }); + + QProgressDialog *m_Progress = + new QProgressDialog(tr("Please Wait - Resolving Symbols"), QString(), 0, 0, this); + m_Progress->setWindowTitle("Please Wait"); + m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint); + m_Progress->setWindowIcon(QIcon()); + m_Progress->setMinimumSize(QSize(250, 0)); + m_Progress->setMaximumSize(QSize(250, 10000)); + m_Progress->setCancelButton(NULL); + m_Progress->setMinimumDuration(0); + m_Progress->setWindowModality(Qt::ApplicationModal); + m_Progress->setValue(0); + + QLabel *label = new QLabel(m_Progress); + + label->setText(tr("Please Wait - Resolving Symbols")); + label->setAlignment(Qt::AlignCenter); + label->setWordWrap(true); + + m_Progress->setLabel(label); + + LambdaThread *thread = new LambdaThread([this, m_Progress]() { + bool running = true; + + while(running) + { + // just bail if we managed to get here without a resolver. + m_Ctx->Renderer()->BlockInvoke( + [&running](IReplayRenderer *r) { running = r->HasCallstacks() && !r->InitResolver(); }); + } + + GUIInvoke::call([this, m_Progress]() { + m_Progress->hide(); + delete m_Progress; + if(m_Ctx->hasAPIInspector()) + m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged(); + }); + }); + thread->selfDelete(true); + thread->start(); +} + void MainWindow::saveLayout_triggered() { LoadSaveLayout(qobject_cast(QObject::sender()), true); diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 79cd1755b..f1c105f27 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -78,10 +78,12 @@ private slots: void on_action_Open_Log_triggered(); void on_action_Close_Log_triggered(); void on_action_Mesh_Output_triggered(); + void on_action_API_Inspector_triggered(); void on_action_Event_Browser_triggered(); void on_action_Texture_Viewer_triggered(); void on_action_Capture_Log_triggered(); void on_action_Inject_into_Process_triggered(); + void on_action_Resolve_Symbols_triggered(); // manual slots void saveLayout_triggered(); diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 8e07002fa..c68061c12 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -110,7 +110,8 @@ SOURCES += 3rdparty/toolwindowmanager/ToolWindowManager.cpp \ Windows/Dialogs/CaptureDialog.cpp \ Code/QRDUtils.cpp \ Windows/Dialogs/LiveCapture.cpp \ - Widgets/Extended/RDListWidget.cpp + Widgets/Extended/RDListWidget.cpp \ + Windows/APIInspector.cpp HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ 3rdparty/toolwindowmanager/ToolWindowManagerArea.h \ @@ -138,7 +139,8 @@ HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ Windows/Dialogs/CaptureDialog.h \ Code/QRDUtils.h \ Windows/Dialogs/LiveCapture.h \ - Widgets/Extended/RDListWidget.h + Widgets/Extended/RDListWidget.h \ + Windows/APIInspector.h FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/MainWindow.ui \ @@ -148,7 +150,8 @@ FORMS += Windows/Dialogs/AboutDialog.ui \ Widgets/ThumbnailStrip.ui \ Windows/Dialogs/TextureSaveDialog.ui \ Windows/Dialogs/CaptureDialog.ui \ - Windows/Dialogs/LiveCapture.ui + Windows/Dialogs/LiveCapture.ui \ + Windows/APIInspector.ui RESOURCES += \ resources.qrc diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index eddab5d33..6d6b3756f 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -285,6 +285,7 @@ + @@ -316,6 +317,7 @@ + @@ -345,6 +347,7 @@ + @@ -364,6 +367,7 @@ + @@ -381,6 +385,7 @@ + @@ -510,4 +515,4 @@ - + \ No newline at end of file diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 564e61ebc..64de2f41d 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -193,6 +193,12 @@ Generated Files + + Generated Files + + + Windows + @@ -294,6 +300,12 @@ Widgets\Extended + + Generated Files + + + Windows + @@ -500,6 +512,9 @@ Windows\Dialogs + + Windows +