Add a compute debugging UI selector to the GL pipeline state view

This commit is contained in:
baldurk
2025-12-02 12:17:36 +00:00
parent 0f70937707
commit 0a15153330
3 changed files with 172 additions and 0 deletions
@@ -29,6 +29,7 @@
#include <QScrollBar>
#include <QXmlStreamWriter>
#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<uint32_t, 3> &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<uint32_t, 3> &group,
const rdcfixedarray<uint32_t, 3> &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<uint32_t, 3> g;
rdcfixedarray<uint32_t, 3> 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);
}
@@ -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<uint32_t, 3> &group,
const rdcfixedarray<uint32_t, 3> &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);
@@ -4744,6 +4744,23 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="computeDebugSelector">
<property name="text">
<string>Debug</string>
</property>
<property name="icon">
<iconset resource="../../Resources/resources.qrc">
<normaloff>:/wrench.png</normaloff>:/wrench.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="csShaderSpacer">
<property name="orientation">