diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index 648eae604..01e26bdab 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -29,6 +29,7 @@ #include #include #include "Code/Resources.h" +#include "Widgets/ComputeDebugSelector.h" #include "Widgets/Extended/RDHeaderView.h" #include "flowlayout/FlowLayout.h" #include "toolwindowmanager/ToolWindowManager.h" @@ -86,6 +87,8 @@ GLPipelineStateViewer::GLPipelineStateViewer(ICaptureContext &ctx, PipelineState { ui->setupUi(this); + m_ComputeDebugSelector = new ComputeDebugSelector(this); + const QIcon &action = Icons::action(); const QIcon &action_hover = Icons::action_hover(); @@ -207,6 +210,9 @@ GLPipelineStateViewer::GLPipelineStateViewer(ICaptureContext &ctx, PipelineState QObject::connect(res, &RDTreeWidget::itemActivated, this, &GLPipelineStateViewer::resource_itemActivated); + QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this, + &GLPipelineStateViewer::computeDebugSelector_beginDebug); + { QMenu *extensionsMenu = new QMenu(this); @@ -1273,6 +1279,8 @@ void GLPipelineStateViewer::clearState() ui->depthBounds->setText(lit("0.0-1.0")); ui->stencils->clear(); + + ui->computeDebugSelector->setEnabled(false); } void GLPipelineStateViewer::setShaderState(const GLPipe::Shader &stage, RDLabel *pipeline, @@ -2591,6 +2599,57 @@ void GLPipelineStateViewer::setState() ui->stencils->clearSelection(); ui->stencils->endUpdate(); + // set up thread debugging inputs + bool enableDebug = m_Ctx.APIProps().shaderDebugging && state.computeShader.reflection && + state.computeShader.reflection->debugInfo.debuggable && action && + (action->flags & ActionFlags::Dispatch); + if(enableDebug) + { + // Validate dispatch/threadgroup dimensions + enableDebug &= action->dispatchDimension[0] > 0; + enableDebug &= action->dispatchDimension[1] > 0; + enableDebug &= action->dispatchDimension[2] > 0; + + const rdcfixedarray &threadDims = + (action->dispatchThreadsDimension[0] == 0) + ? state.computeShader.reflection->dispatchThreadsDimension + : action->dispatchThreadsDimension; + enableDebug &= threadDims[0] > 0; + enableDebug &= threadDims[1] > 0; + enableDebug &= threadDims[2] > 0; + } + + if(enableDebug) + { + ui->computeDebugSelector->setEnabled(true); + + // set maximums for CS debugging + m_ComputeDebugSelector->SetThreadBounds( + action->dispatchDimension, (action->dispatchThreadsDimension[0] == 0) + ? state.computeShader.reflection->dispatchThreadsDimension + : action->dispatchThreadsDimension); + + ui->computeDebugSelector->setToolTip( + tr("Debug this compute shader by specifying group/thread ID or dispatch ID")); + } + else + { + ui->computeDebugSelector->setEnabled(false); + + if(!m_Ctx.APIProps().shaderDebugging) + ui->computeDebugSelector->setToolTip(tr("This API does not support shader debugging")); + else if(!action || !(action->flags & ActionFlags::Dispatch)) + ui->computeDebugSelector->setToolTip(tr("No dispatch selected")); + else if(!state.computeShader.reflection) + ui->computeDebugSelector->setToolTip(tr("No compute shader bound")); + else if(!state.computeShader.reflection->debugInfo.debuggable) + ui->computeDebugSelector->setToolTip( + tr("This shader doesn't support debugging: %1") + .arg(state.computeShader.reflection->debugInfo.debugStatus)); + else + ui->computeDebugSelector->setToolTip(tr("Invalid dispatch/threadgroup dimensions.")); + } + // highlight the appropriate stages in the flowchart if(action == NULL) { @@ -3942,3 +4001,93 @@ void GLPipelineStateViewer::on_meshView_clicked() m_Ctx.ShowMeshPreview(); ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget()); } + +void GLPipelineStateViewer::on_computeDebugSelector_clicked() +{ + // Check whether debugging is valid for this event before showing the dialog + if(!m_Ctx.IsCaptureLoaded()) + return; + + const ActionDescription *action = m_Ctx.CurAction(); + + if(!action) + return; + + const ShaderReflection *shaderDetails = + m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); + + if(!shaderDetails) + return; + + RDDialog::show(m_ComputeDebugSelector); +} + +void GLPipelineStateViewer::computeDebugSelector_beginDebug(const rdcfixedarray &group, + const rdcfixedarray &thread) +{ + const ActionDescription *action = m_Ctx.CurAction(); + + if(!action) + return; + + const ShaderReflection *shaderDetails = + m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); + + if(!shaderDetails) + return; + + struct threadSelect + { + rdcfixedarray g; + rdcfixedarray t; + } debugThread = { + // g[] + {group[0], group[1], group[2]}, + // t[] + {thread[0], thread[1], thread[2]}, + }; + + bool done = false; + ShaderDebugTrace *trace = NULL; + + m_Ctx.Replay().AsyncInvoke([&trace, &done, debugThread](IReplayController *r) { + trace = r->DebugThread(debugThread.g, debugThread.t); + + if(trace->debugger == NULL) + { + r->FreeTrace(trace); + trace = NULL; + } + + done = true; + }); + + QString debugContext = lit("Group [%1,%2,%3] Thread [%4,%5,%6]") + .arg(group[0]) + .arg(group[1]) + .arg(group[2]) + .arg(thread[0]) + .arg(thread[1]) + .arg(thread[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( + shaderDetails, m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); + + m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); +} diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h index 5c9500d6b..74dd6c69e 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h @@ -34,6 +34,7 @@ class GLPipelineStateViewer; class QXmlStreamWriter; +class ComputeDebugSelector; class RDLabel; class RDTreeWidget; class RDTreeWidgetItem; @@ -83,10 +84,15 @@ private slots: void ubo_itemActivated(RDTreeWidgetItem *item, int column); void vertex_leave(QEvent *e); + void on_computeDebugSelector_clicked(); + void computeDebugSelector_beginDebug(const rdcfixedarray &group, + const rdcfixedarray &thread); + private: Ui::GLPipelineStateViewer *ui; ICaptureContext &m_Ctx; PipelineStateViewer &m_Common; + ComputeDebugSelector *m_ComputeDebugSelector; QString MakeGenericValueString(uint32_t compCount, CompType compType, const GLPipe::VertexAttribute &val); diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui index fb7763e9a..06f4caa96 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui @@ -4744,6 +4744,23 @@ + + + + Debug + + + + :/wrench.png:/wrench.png + + + Qt::ToolButtonTextBesideIcon + + + true + + +