diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index a86f63fa3..ac8d7d9c4 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -34,6 +34,7 @@ #include #include #include "Windows/APIInspector.h" +#include "Windows/BufferViewer.h" #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/LiveCapture.h" #include "Windows/EventBrowser.h" @@ -455,6 +456,18 @@ TextureViewer *CaptureContext::textureViewer() return m_TextureViewer; } +BufferViewer *CaptureContext::meshPreview() +{ + if(m_MeshPreview) + return m_MeshPreview; + + m_MeshPreview = new BufferViewer(this, m_MainWindow); + m_MeshPreview->setObjectName("meshPreview"); + m_MeshPreview->setWindowIcon(*m_Icon); + + return m_MeshPreview; +} + PipelineStateViewer *CaptureContext::pipelineViewer() { if(m_PipelineViewer) @@ -502,6 +515,11 @@ void CaptureContext::showTextureViewer() m_MainWindow->showTextureViewer(); } +void CaptureContext::showMeshPreview() +{ + m_MainWindow->showMeshPreview(); +} + void CaptureContext::showPipelineViewer() { m_MainWindow->showPipelineViewer(); @@ -526,6 +544,10 @@ QWidget *CaptureContext::createToolWindow(const QString &objectName) { return pipelineViewer(); } + else if(objectName == "meshPreview") + { + return meshPreview(); + } else if(objectName == "apiInspector") { return apiInspector(); @@ -550,6 +572,8 @@ void CaptureContext::windowClosed(QWidget *window) m_APIInspector = NULL; else if((QWidget *)m_PipelineViewer == window) m_PipelineViewer = NULL; + else if((QWidget *)m_MeshPreview == window) + m_MeshPreview = NULL; else qCritical() << "Unrecognised window being closed: " << window; } diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 8e156f7ee..5cfcb8522 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -49,6 +49,7 @@ class MainWindow; class EventBrowser; class APIInspector; class PipelineStateViewer; +class BufferViewer; class TextureViewer; class CaptureDialog; class QProgressDialog; @@ -121,6 +122,7 @@ public: EventBrowser *eventBrowser(); APIInspector *apiInspector(); TextureViewer *textureViewer(); + BufferViewer *meshPreview(); PipelineStateViewer *pipelineViewer(); CaptureDialog *captureDialog(); @@ -128,10 +130,12 @@ public: bool hasAPIInspector() { return m_APIInspector != NULL; } bool hasTextureViewer() { return m_TextureViewer != NULL; } bool hasPipelineViewer() { return m_PipelineViewer != NULL; } + bool hasMeshPreview() { return m_MeshPreview != NULL; } bool hasCaptureDialog() { return m_CaptureDialog != NULL; } void showEventBrowser(); void showAPIInspector(); void showTextureViewer(); + void showMeshPreview(); void showPipelineViewer(); void showCaptureDialog(); @@ -202,6 +206,7 @@ private: EventBrowser *m_EventBrowser = NULL; APIInspector *m_APIInspector = NULL; TextureViewer *m_TextureViewer = NULL; + BufferViewer *m_MeshPreview = NULL; PipelineStateViewer *m_PipelineViewer = NULL; CaptureDialog *m_CaptureDialog = NULL; }; diff --git a/qrenderdoc/Code/FormatElement.cpp b/qrenderdoc/Code/FormatElement.cpp index f2d581734..dd35db03e 100644 --- a/qrenderdoc/Code/FormatElement.cpp +++ b/qrenderdoc/Code/FormatElement.cpp @@ -445,7 +445,7 @@ inline T readObj(const byte *&data, const byte *end, bool &ok) return ret; } -QVariantList FormatElement::GetVariants(const byte *&data, const byte *end) +QVariantList FormatElement::GetVariants(const byte *&data, const byte *end) const { QVariantList ret; @@ -708,7 +708,7 @@ QVariantList FormatElement::GetVariants(const byte *&data, const byte *end) return ret; } -ShaderVariable FormatElement::GetShaderVar(const byte *&data, const byte *end) +ShaderVariable FormatElement::GetShaderVar(const byte *&data, const byte *end) const { QVariantList objs = GetVariants(data, end); diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 0e985cb8c..063d60c7d 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -323,8 +323,8 @@ struct FormatElement static QList ParseFormatString(const QString &formatString, uint64_t maxLen, bool tightPacking, QString &errors); - QVariantList GetVariants(const byte *&data, const byte *end); - ShaderVariable GetShaderVar(const byte *&data, const byte *end); + QVariantList GetVariants(const byte *&data, const byte *end) const; + ShaderVariable GetShaderVar(const byte *&data, const byte *end) const; QString ElementString(const QVariant &var); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp new file mode 100644 index 000000000..650fef04f --- /dev/null +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -0,0 +1,272 @@ +/****************************************************************************** + * 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 "BufferViewer.h" +#include +#include "ui_BufferViewer.h" + +struct VBData +{ + byte *data; + byte *end; + size_t stride; +}; + +class BufferItemModel : public QAbstractItemModel +{ +public: + BufferItemModel(QObject *parent) : QAbstractItemModel(parent) {} + void beginReset() { emit beginResetModel(); } + void endReset() { emit endResetModel(); } + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override + { + if(row < 0 || row >= rowCount()) + return QModelIndex(); + + return createIndex(row, column); + } + + QModelIndex parent(const QModelIndex &index) const override { return QModelIndex(); } + int rowCount(const QModelIndex &parent = QModelIndex()) const override { return numRows; } + int columnCount(const QModelIndex &parent = QModelIndex()) const override + { + return columns.count(); + } + Qt::ItemFlags flags(const QModelIndex &index) const override + { + if(!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); + } + + QVariant headerData(int section, Qt::Orientation orientation, int role) const override + { + if(section < columns.count() && orientation == Qt::Horizontal && role == Qt::DisplayRole) + return columns[section].name; + + return QVariant(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if(index.isValid()) + { + if(role == Qt::DisplayRole) + { + int row = index.row(); + int col = index.column(); + + if(col >= 0 && col < columns.count() && row >= 0 && row < numRows) + { + const FormatElement &el = columns[col]; + + if(el.buffer < buffers.size()) + { + const byte *data = buffers[el.buffer].data; + const byte *end = buffers[el.buffer].end; + + data += buffers[el.buffer].stride * row; + + data += el.offset; + + QVariantList list = el.GetVariants(data, end); + + QString ret; + + for(QVariant &v : list) + { + if(v.type() == QMetaType::Double) + ret += Formatter::Format(v.toDouble()); + else if(v.type() == QMetaType::Float) + ret += Formatter::Format(v.toFloat()); + else if(v.type() == QMetaType::UInt || v.type() == QMetaType::UShort || + v.type() == QMetaType::UChar) + ret += Formatter::Format(v.toUInt()); + else if(v.type() == QMetaType::Int || v.type() == QMetaType::Short || + v.type() == QMetaType::SChar) + ret += Formatter::Format(v.toInt()); + else + ret += v.toString(); + + ret += " "; + } + + return ret.trimmed(); + } + } + } + } + + return QVariant(); + } + + int numRows; + QList columns; + QList buffers; +}; + +BufferViewer::BufferViewer(CaptureContext *ctx, QWidget *parent) + : QFrame(parent), ui(new Ui::BufferViewer), m_Ctx(ctx) +{ + ui->setupUi(this); + m_Ctx->AddLogViewer(this); + + ui->vsinData->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + + Reset(); +} + +BufferViewer::~BufferViewer() +{ + m_Ctx->windowClosed(this); + m_Ctx->RemoveLogViewer(this); + delete ui; +} + +BufferItemModel *model = NULL; + +void BufferViewer::OnLogfileLoaded() +{ + Reset(); + + model = new BufferItemModel(this); + + ui->vsinData->setModel(model); + + WId renderID = ui->render->winId(); + + m_Ctx->Renderer()->BlockInvoke([renderID, this](IReplayRenderer *r) { + m_Output = r->CreateOutput(m_Ctx->m_CurWinSystem, m_Ctx->FillWindowingData(renderID), + eOutputType_MeshDisplay); + + ui->render->setOutput(m_Output); + + OutputConfig c = {eOutputType_MeshDisplay}; + m_Output->SetOutputConfig(c); + + GUIInvoke::call([this]() { OnEventSelected(m_Ctx->CurEvent()); }); + }); +} + +void BufferViewer::OnLogfileClosed() +{ + Reset(); +} + +void BufferViewer::OnEventSelected(uint32_t eventID) +{ + if(model) + { + model->beginReset(); + + QVector vinputs = m_Ctx->CurPipelineState.GetVertexInputs(); + + model->columns.clear(); + model->columns.reserve(vinputs.count()); + + for(const VertexInputAttribute &a : vinputs) + { + if(!a.Used) + continue; + + FormatElement f(a.Name, a.VertexBuffer, a.RelativeByteOffset, a.PerInstance, a.InstanceRate, + false, // row major matrix + 1, // matrix dimension + a.Format, false); + + model->columns.push_back(f); + } + + for(auto vb : model->buffers) + delete[] vb.data; + + model->buffers.clear(); + + const FetchDrawcall *draw = m_Ctx->CurDrawcall(); + + if(draw == NULL) + model->numRows = 0; + else + model->numRows = draw->numIndices; + + QVector vbs = m_Ctx->CurPipelineState.GetVBuffers(); + + m_Ctx->Renderer()->AsyncInvoke([this, vbs](IReplayRenderer *r) { + + for(BoundVBuffer vb : vbs) + { + rdctype::array data; + r->GetBufferData(vb.Buffer, vb.ByteOffset, 0, &data); + + VBData buf; + buf.data = new byte[data.count]; + memcpy(buf.data, data.elems, data.count); + buf.end = buf.data + data.count; + buf.stride = vb.ByteStride; + model->buffers.push_back(buf); + } + + GUIInvoke::call([this] { + model->endReset(); + ui->vsinData->resizeColumnsToContents(); + }); + }); + } +} + +void BufferViewer::Reset() +{ + m_Output = NULL; + + if(model) + { + for(auto vb : model->buffers) + delete[] vb.data; + + model->buffers.clear(); + + model->columns.clear(); + + model->numRows = 0; + } + + CaptureContext *ctx = m_Ctx; + + // while a log is loaded, pass NULL into the widget + if(!ctx->LogLoaded()) + ctx = NULL; + + { + CustomPaintWidget *render = new CustomPaintWidget(ctx, this); + render->setObjectName(ui->render->objectName()); + render->setSizePolicy(ui->render->sizePolicy()); + delete ui->render; + ui->render = render; + ui->horizontalLayout->addWidget(ui->render); + } + + ui->render->setColours(QColor::fromRgbF(0.57f, 0.57f, 0.57f, 1.0f), + QColor::fromRgbF(0.81f, 0.81f, 0.81f, 1.0f)); +} diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h new file mode 100644 index 000000000..425709e5a --- /dev/null +++ b/qrenderdoc/Windows/BufferViewer.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * 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 BufferViewer; +} + +class BufferViewer : public QFrame, public ILogViewerForm +{ + Q_OBJECT + +public: + explicit BufferViewer(CaptureContext *ctx, QWidget *parent = 0); + ~BufferViewer(); + + void OnLogfileLoaded(); + void OnLogfileClosed(); + void OnEventSelected(uint32_t eventID); + +private: + Ui::BufferViewer *ui; + CaptureContext *m_Ctx; + + IReplayOutput *m_Output; + + void Reset(); +}; diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui new file mode 100644 index 000000000..797f3ed78 --- /dev/null +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -0,0 +1,100 @@ + + + BufferViewer + + + + 0 + 0 + 612 + 429 + + + + Buffer Viewer + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + 0 + + + + VS In + + + + + + + + + + VS Out + + + + + + + + + CustomPaintWidget + QWidget +
Widgets/CustomPaintWidget.h
+
+
+ + +
diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index b198b7a75..1bcab124f 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -36,6 +36,7 @@ #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/LiveCapture.h" #include "APIInspector.h" +#include "BufferViewer.h" #include "ConstantBufferPreviewer.h" #include "EventBrowser.h" #include "TextureViewer.h" @@ -155,6 +156,12 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main pipe, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->toolWindowManager->areaOf(textureViewer))); + BufferViewer *mesh = m_Ctx->meshPreview(); + + ui->toolWindowManager->addToolWindow( + mesh, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->toolWindowManager->areaOf(textureViewer))); + CaptureDialog *capDialog = m_Ctx->captureDialog(); ui->toolWindowManager->addToolWindow( @@ -708,6 +715,10 @@ ToolWindowManager::AreaReference MainWindow::mainToolArea() ui->toolWindowManager->toolWindows().contains(m_Ctx->pipelineViewer())) return ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->toolWindowManager->areaOf(m_Ctx->pipelineViewer())); + else if(m_Ctx->hasMeshPreview() && + ui->toolWindowManager->toolWindows().contains(m_Ctx->meshPreview())) + return ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->toolWindowManager->areaOf(m_Ctx->meshPreview())); else if(m_Ctx->hasCaptureDialog() && ui->toolWindowManager->toolWindows().contains(m_Ctx->captureDialog())) return ToolWindowManager::AreaReference(ToolWindowManager::AddTo, @@ -942,7 +953,12 @@ void MainWindow::on_action_About_triggered() void MainWindow::on_action_Mesh_Output_triggered() { - // TODO Mesh view + BufferViewer *meshPreview = m_Ctx->meshPreview(); + + if(ui->toolWindowManager->toolWindows().contains(meshPreview)) + ToolWindowManager::raiseToolWindow(meshPreview); + else + ui->toolWindowManager->addToolWindow(meshPreview, mainToolArea()); } void MainWindow::on_action_API_Inspector_triggered() diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index bb223c430..894152a09 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -73,7 +73,7 @@ public: void showEventBrowser() { on_action_Event_Browser_triggered(); } void showAPIInspector() { on_action_API_Inspector_triggered(); } - void showMeshOutput() { on_action_Mesh_Output_triggered(); } + void showMeshPreview() { on_action_Mesh_Output_triggered(); } void showTextureViewer() { on_action_Texture_Viewer_triggered(); } void showPipelineViewer() { on_action_Pipeline_State_triggered(); } void showCaptureDialog() { on_action_Capture_Log_triggered(); } diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 74dbca33d..4785de8d7 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -26,6 +26,7 @@ #include #include #include "3rdparty/toolwindowmanager/ToolWindowManager.h" +#include "Windows/BufferViewer.h" #include "Windows/ConstantBufferPreviewer.h" #include "Windows/MainWindow.h" #include "Windows/TextureViewer.h" @@ -2153,10 +2154,7 @@ void VulkanPipelineStateViewer::ubo_itemActivated(QTreeWidgetItem *item, int col void VulkanPipelineStateViewer::on_viAttrs_itemActivated(QTreeWidgetItem *item, int column) { - // TODO Buffer Viewer - // if(!m_Ctx->hasMeshOutput()) - // m_Ctx->showMeshOutput(); - // ToolWindowManager::raiseToolWindow(m_Ctx->meshOutput()); + on_meshView_clicked(); } void VulkanPipelineStateViewer::on_viBuffers_itemActivated(QTreeWidgetItem *item, int column) @@ -2333,4 +2331,7 @@ void VulkanPipelineStateViewer::on_exportHTML_clicked() void VulkanPipelineStateViewer::on_meshView_clicked() { + if(!m_Ctx->hasMeshPreview()) + m_Ctx->showMeshPreview(); + ToolWindowManager::raiseToolWindow(m_Ctx->meshPreview()); } diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index a32ed6268..4372f3584 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -120,7 +120,8 @@ SOURCES += 3rdparty/toolwindowmanager/ToolWindowManager.cpp \ Widgets/Extended/RDTreeWidget.cpp \ Windows/ConstantBufferPreviewer.cpp \ Widgets/BufferFormatSpecifier.cpp \ - Code/FormatElement.cpp + Code/FormatElement.cpp \ + Windows/BufferViewer.cpp HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ 3rdparty/toolwindowmanager/ToolWindowManagerArea.h \ @@ -157,7 +158,8 @@ HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ Windows/PipelineState/GLPipelineStateViewer.h \ Widgets/Extended/RDTreeWidget.h \ Windows/ConstantBufferPreviewer.h \ - Widgets/BufferFormatSpecifier.h + Widgets/BufferFormatSpecifier.h \ + Windows/BufferViewer.h FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/MainWindow.ui \ @@ -175,7 +177,8 @@ FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/PipelineState/D3D12PipelineStateViewer.ui \ Windows/PipelineState/GLPipelineStateViewer.ui \ Windows/ConstantBufferPreviewer.ui \ - Widgets/BufferFormatSpecifier.ui + Widgets/BufferFormatSpecifier.ui \ + Windows/BufferViewer.ui RESOURCES += \ resources.qrc diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index a5f61e12d..62c77a530 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -288,6 +288,7 @@ + @@ -329,6 +330,7 @@ + @@ -366,6 +368,7 @@ + @@ -394,6 +397,7 @@ + @@ -419,6 +423,7 @@ + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 7dd74c17e..77c03e0ba 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -253,6 +253,12 @@ Generated Files + + Generated Files + + + Windows + @@ -405,6 +411,12 @@ Generated Files + + Generated Files + + + Windows + @@ -635,6 +647,9 @@ Widgets + + Windows +