diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index d60f245ba..58cf35c11 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -30,6 +30,7 @@ #include #include "Code/Resources.h" #include "Widgets/Extended/RDHeaderView.h" +#include "flowlayout/FlowLayout.h" #include "toolwindowmanager/ToolWindowManager.h" #include "PipelineStateViewer.h" #include "ui_VulkanPipelineStateViewer.h" @@ -135,6 +136,23 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx, ui->vsUBOs, ui->tcsUBOs, ui->tesUBOs, ui->gsUBOs, ui->fsUBOs, ui->csUBOs, }; + // setup FlowLayout for CS shader group, with debugging controls + { + QLayout *oldLayout = ui->csShaderGroup->layout(); + + QObjectList childs = ui->csShaderGroup->children(); + childs.removeOne((QObject *)oldLayout); + + delete oldLayout; + + FlowLayout *csShaderFlow = new FlowLayout(ui->csShaderGroup, -1, 3, 3); + + for(QObject *o : childs) + csShaderFlow->addWidget(qobject_cast(o)); + + ui->csShaderGroup->setLayout(csShaderFlow); + } + for(QToolButton *b : viewButtons) QObject::connect(b, &QToolButton::clicked, this, &VulkanPipelineStateViewer::shaderView_clicked); @@ -749,6 +767,18 @@ void VulkanPipelineStateViewer::clearState() ui->stencils->clear(); + { + ui->groupX->setEnabled(false); + ui->groupY->setEnabled(false); + ui->groupZ->setEnabled(false); + + ui->threadX->setEnabled(false); + ui->threadY->setEnabled(false); + ui->threadZ->setEnabled(false); + + ui->debugThread->setEnabled(false); + } + ui->conditionalRenderingGroup->setVisible(false); ui->csConditionalRenderingGroup->setVisible(false); } @@ -2501,6 +2531,51 @@ void VulkanPipelineStateViewer::setState() ui->stencils->clearSelection(); ui->stencils->endUpdate(); + // set up thread debugging inputs + if(m_Ctx.APIProps().shaderDebugging && state.computeShader.reflection && draw && + (draw->flags & DrawFlags::Dispatch)) + { + ui->groupX->setEnabled(true); + ui->groupY->setEnabled(true); + ui->groupZ->setEnabled(true); + + ui->threadX->setEnabled(true); + ui->threadY->setEnabled(true); + ui->threadZ->setEnabled(true); + + ui->debugThread->setEnabled(true); + + // set maximums for CS debugging + ui->groupX->setMaximum((int)draw->dispatchDimension[0] - 1); + ui->groupY->setMaximum((int)draw->dispatchDimension[1] - 1); + ui->groupZ->setMaximum((int)draw->dispatchDimension[2] - 1); + + if(draw->dispatchThreadsDimension[0] == 0) + { + ui->threadX->setMaximum((int)state.computeShader.reflection->dispatchThreadsDimension[0] - 1); + ui->threadY->setMaximum((int)state.computeShader.reflection->dispatchThreadsDimension[1] - 1); + ui->threadZ->setMaximum((int)state.computeShader.reflection->dispatchThreadsDimension[2] - 1); + } + else + { + ui->threadX->setMaximum((int)draw->dispatchThreadsDimension[0] - 1); + ui->threadY->setMaximum((int)draw->dispatchThreadsDimension[1] - 1); + ui->threadZ->setMaximum((int)draw->dispatchThreadsDimension[2] - 1); + } + } + else + { + ui->groupX->setEnabled(false); + ui->groupY->setEnabled(false); + ui->groupZ->setEnabled(false); + + ui->threadX->setEnabled(false); + ui->threadY->setEnabled(false); + ui->threadZ->setEnabled(false); + + ui->debugThread->setEnabled(false); + } + // highlight the appropriate stages in the flowchart if(draw == NULL) { @@ -3722,3 +3797,94 @@ void VulkanPipelineStateViewer::on_meshView_clicked() m_Ctx.ShowMeshPreview(); ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget()); } + +void VulkanPipelineStateViewer::on_debugThread_clicked() +{ + if(!m_Ctx.APIProps().shaderDebugging) + return; + + if(!m_Ctx.IsCaptureLoaded()) + return; + + const DrawcallDescription *draw = m_Ctx.CurDrawcall(); + + if(!draw || !(draw->flags & DrawFlags::Dispatch)) + return; + + ShaderReflection *shaderDetails = m_Ctx.CurVulkanPipelineState()->computeShader.reflection; + const ShaderBindpointMapping &bindMapping = + m_Ctx.CurVulkanPipelineState()->computeShader.bindpointMapping; + + if(!shaderDetails) + return; + + uint32_t groupdim[3] = {}; + + for(int i = 0; i < 3; i++) + groupdim[i] = draw->dispatchDimension[i]; + + uint32_t threadsdim[3] = {}; + for(int i = 0; i < 3; i++) + threadsdim[i] = draw->dispatchThreadsDimension[i]; + + if(threadsdim[0] == 0) + { + for(int i = 0; i < 3; i++) + threadsdim[i] = shaderDetails->dispatchThreadsDimension[i]; + } + + struct threadSelect + { + uint32_t g[3]; + uint32_t t[3]; + } thread = { + // g[] + {(uint32_t)ui->groupX->value(), (uint32_t)ui->groupY->value(), (uint32_t)ui->groupZ->value()}, + // t[] + {(uint32_t)ui->threadX->value(), (uint32_t)ui->threadY->value(), (uint32_t)ui->threadZ->value()}, + }; + + bool done = false; + ShaderDebugTrace *trace = NULL; + + m_Ctx.Replay().AsyncInvoke([&trace, &done, thread](IReplayController *r) { + trace = r->DebugThread(thread.g, thread.t); + + if(trace->debugger == NULL) + { + r->FreeTrace(trace); + trace = NULL; + } + + done = true; + }); + + QString debugContext = lit("Group [%1,%2,%3] Thread [%4,%5,%6]") + .arg(thread.g[0]) + .arg(thread.g[1]) + .arg(thread.g[2]) + .arg(thread.t[0]) + .arg(thread.t[1]) + .arg(thread.t[2]); + + // wait a short while before displaying the progress dialog (which won't show if we're already + // done by the time we reach it) + for(int i = 0; !done && i < 100; i++) + QThread::msleep(5); + + ShowProgressDialog(this, tr("Debugging %1").arg(debugContext), [&done]() { return done; }); + + if(!trace) + { + RDDialog::critical( + this, tr("Error debugging"), + tr("Error debugging thread - make sure a valid group and thread is selected")); + return; + } + + // viewer takes ownership of the trace + IShaderViewer *s = + m_Ctx.DebugShader(&bindMapping, shaderDetails, ResourceId(), trace, debugContext); + + m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); +} diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h index e63479781..437300c78 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h @@ -82,6 +82,8 @@ private slots: void ubo_itemActivated(RDTreeWidgetItem *item, int column); void vertex_leave(QEvent *e); + void on_debugThread_clicked(); + private: Ui::VulkanPipelineStateViewer *ui; ICaptureContext &m_Ctx; diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui index eef44c202..e07598783 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui @@ -3546,90 +3546,191 @@ 4 - - - QFrame::Box - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::Box + + + + + + + PointingHandCursor + + + Open Shader Source + + + View + + + + :/action.png:/action.png + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Edit Shader + + + Edit + + + + :/page_white_edit.png:/page_white_edit.png + + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Save Shader DXBC + + + Save + + + + :/save.png:/save.png + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + - - - PointingHandCursor - - - Open Shader Source - - - View - - - - :/action.png:/action.png - - - Qt::ToolButtonTextBesideIcon - - - true - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Debug Group: + + + 20 + + + + + + + + + + + + + + + + Thread: + + + 20 + + + + + + + + + + + + + + + + + + + + :/wrench.png:/wrench.png + + + + true + + + + - - - Edit Shader - - - Edit - - - - :/page_white_edit.png:/page_white_edit.png - - - QToolButton::MenuButtonPopup - - - Qt::ToolButtonTextBesideIcon - - - true - - - - - - - Save Shader SPIR-V - - - Save - - - - :/save.png:/save.png - - - Qt::ToolButtonTextBesideIcon - - - true - - - - - + Qt::Horizontal - 0 - 0 + 40 + 20