Add a dialog for directly-launching pixel shader debug. Closes #3422

This commit is contained in:
baldurk committed 2026-11-04 12:25:03 +00:00
1 parent 282b28b3a3
commit fd09bb3f4c
26 files changed
+802 -614

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 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 "PixelDebugSelector.h"
#include "Code/QRDUtils.h"
#include "ui_PixelDebugSelector.h"
PixelDebugSelector::PixelDebugSelector(ICaptureContext &ctx, QWidget *parent)
: QDialog(parent), ui(new Ui::PixelDebugSelector), m_Ctx(ctx)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->x->setFont(Formatter::PreferredFont());
ui->y->setFont(Formatter::PreferredFont());
ui->primitive->setFont(Formatter::PreferredFont());
ui->sample->setFont(Formatter::PreferredFont());
ui->view->setFont(Formatter::PreferredFont());
}
PixelDebugSelector::~PixelDebugSelector()
{
delete ui;
}
void PixelDebugSelector::UpdateTarget()
{
const ActionDescription *action = m_Ctx.CurAction();
if(!action)
return;
const PipeState &pipe = m_Ctx.CurPipelineState();
ui->view->setMaximum(pipe.MultiviewBroadcastCount() - 1);
ui->viewLabel->setVisible(ui->view->maximum() > 1);
ui->view->setVisible(ui->view->maximum() > 1);
uint32_t sampleCount = pipe.GetOverrideSampleCount();
if(sampleCount == 0)
{
for(const Descriptor &d : pipe.GetOutputTargets())
{
const TextureDescription *tex = m_Ctx.GetTexture(d.resource);
if(tex)
sampleCount = qMax(sampleCount, tex->msSamp);
}
}
ui->sample->setMaximum(pipe.GetOverrideSampleCount() - 1);
ui->sampleLabel->setVisible(ui->sample->maximum() > 1);
ui->sample->setVisible(ui->sample->maximum() > 1);
}
void PixelDebugSelector::on_selectedPixel_clicked()
{
rdcpair<int32_t, int32_t> sel = m_Ctx.GetTextureViewer()->GetPickedLocation();
ui->x->setValue(sel.first);
ui->y->setValue(sel.second);
}
void PixelDebugSelector::on_beginDebug_clicked()
{
DebugPixelInputs inputs;
bool ok = false;
inputs.primitive = ui->primitive->text().toUInt(&ok);
if(!ok)
inputs.primitive = ~0U;
inputs.sample = ui->sample->value();
inputs.view = ui->view->value();
emit beginDebug(ui->x->value(), ui->y->value(), inputs);
close();
}
void PixelDebugSelector::on_cancelDebug_clicked()
{
close();
}
+63
View File
@@ -0,0 +1,63 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021-2026 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 <QDialog>
#include "Code/Interface/QRDInterface.h"
namespace Ui
{
class PixelDebugSelector;
}
class PixelDebugSelector : public QDialog
{
Q_OBJECT
public:
explicit PixelDebugSelector(ICaptureContext &ctx, QWidget *parent = 0);
~PixelDebugSelector();
void UpdateTarget();
public slots:
signals:
void beginDebug(uint32_t x, uint32_t y, const DebugPixelInputs &inputs);
private slots:
// automatic slots
void on_selectedPixel_clicked();
void on_beginDebug_clicked();
void on_cancelDebug_clicked();
private:
void SyncGroupThreadValue();
void SyncDispatchThreadValue();
Ui::PixelDebugSelector *ui;
ICaptureContext &m_Ctx;
};
+164
View File
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PixelDebugSelector</class>
<widget class="QDialog" name="PixelDebugSelector">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>176</width>
<height>274</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Debug Pixel Shader</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Pixel co-ordinate</string>
</property>
<layout class="QGridLayout" name="gridLayout_groupthread">
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="xLabel">
<property name="text">
<string>X:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="y">
<property name="maximum">
<number>16384</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="yLabel">
<property name="text">
<string>Y:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="x">
<property name="maximum">
<number>16384</number>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="selectedPixel">
<property name="text">
<string>Use Selected Pixel</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/wand.png</normaloff>:/wand.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox1">
<property name="title">
<string>Disambiguation</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="primLabel">
<property name="text">
<string>Primitive:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="sample"/>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="view"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sampleLabel">
<property name="text">
<string>Sample:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="viewLabel">
<property name="text">
<string>Multiview:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="RDLineEdit" name="primitive">
<property name="placeholderText">
<string>Any</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_buttons">
<item>
<widget class="QPushButton" name="beginDebug">
<property name="text">
<string>Debug</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/wrench.png</normaloff>:/wrench.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelDebug">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RDLineEdit</class>
<extends>QLineEdit</extends>
<header>Widgets/Extended/RDLineEdit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../Resources/resources.qrc"/>
</resources>
<connections/>
</ui>
@@ -29,7 +29,6 @@
#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"
@@ -78,8 +77,6 @@ D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx,
{
ui->setupUi(this);
m_ComputeDebugSelector = new ComputeDebugSelector(this);
const QIcon &action = Icons::action();
const QIcon &action_hover = Icons::action_hover();
@@ -163,8 +160,10 @@ D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx,
b->setMinimumSizeHint(QSize(250, 0));
}
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&D3D11PipelineStateViewer::computeDebugSelector_beginDebug);
QObject::connect(ui->computeDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::computeDebugSelector_clicked);
QObject::connect(ui->pixelDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::pixelDebugSelector_clicked);
for(QToolButton *b : editButtons)
QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked);
@@ -486,7 +485,6 @@ D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx,
D3D11PipelineStateViewer::~D3D11PipelineStateViewer()
{
delete ui;
delete m_ComputeDebugSelector;
}
void D3D11PipelineStateViewer::OnCaptureLoaded()
@@ -1195,6 +1193,7 @@ void D3D11PipelineStateViewer::clearState()
ui->predicateGroup->setVisible(false);
ui->computeDebugSelector->setEnabled(false);
ui->pixelDebugSelector->setEnabled(false);
}
void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, RDLabel *shader,
@@ -2099,56 +2098,7 @@ void D3D11PipelineStateViewer::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."));
}
m_Common.SetupDebugSelectors(ui->computeDebugSelector, ui->pixelDebugSelector);
// highlight the appropriate stages in the flowchart
if(action == NULL)
@@ -3437,93 +3387,3 @@ void D3D11PipelineStateViewer::on_meshView_clicked()
m_Ctx.ShowMeshPreview();
ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget());
}
void D3D11PipelineStateViewer::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 D3D11PipelineStateViewer::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,7 +34,6 @@ class D3D11PipelineStateViewer;
class QXmlStreamWriter;
class ComputeDebugSelector;
class RDLabel;
class RDTreeWidget;
class RDTreeWidgetItem;
@@ -79,15 +78,10 @@ private slots:
void cbuffer_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::D3D11PipelineStateViewer *ui;
ICaptureContext &m_Ctx;
PipelineStateViewer &m_Common;
ComputeDebugSelector *m_ComputeDebugSelector;
void setShaderState(const D3D11Pipe::Shader &stage, RDLabel *shader, RDLabel *shaderDebug,
RDTreeWidget *tex, RDTreeWidget *samp, RDTreeWidget *cbuffer,
@@ -3010,6 +3010,23 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pixelDebugSelector">
<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="psShaderSpacer">
<property name="orientation">
@@ -29,7 +29,6 @@
#include <QScrollBar>
#include <QXmlStreamWriter>
#include "Code/Resources.h"
#include "Widgets/ComputeDebugSelector.h"
#include "Widgets/Extended/RDHeaderView.h"
#include "Windows/DescriptorViewer.h"
#include "flowlayout/FlowLayout.h"
@@ -100,8 +99,6 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx,
{
ui->setupUi(this);
m_ComputeDebugSelector = new ComputeDebugSelector(this);
const QIcon &action = Icons::action();
const QIcon &action_hover = Icons::action_hover();
@@ -217,8 +214,10 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx,
ui->predicate->setMinimumSizeHint(QSize(250, 0));
}
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&D3D12PipelineStateViewer::computeDebugSelector_beginDebug);
QObject::connect(ui->computeDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::computeDebugSelector_clicked);
QObject::connect(ui->pixelDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::pixelDebugSelector_clicked);
for(QToolButton *b : editButtons)
QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked);
@@ -510,7 +509,6 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx,
D3D12PipelineStateViewer::~D3D12PipelineStateViewer()
{
delete ui;
delete m_ComputeDebugSelector;
}
void D3D12PipelineStateViewer::OnCaptureLoaded()
@@ -1196,6 +1194,7 @@ void D3D12PipelineStateViewer::clearState()
ui->predicateGroup->setVisible(false);
ui->computeDebugSelector->setEnabled(false);
ui->pixelDebugSelector->setEnabled(false);
}
void D3D12PipelineStateViewer::setShaderState(const D3D12Pipe::Shader &stage, RDLabel *pipeline,
@@ -2307,56 +2306,7 @@ void D3D12PipelineStateViewer::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."));
}
m_Common.SetupDebugSelectors(ui->computeDebugSelector, ui->pixelDebugSelector);
}
void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column)
@@ -3701,96 +3651,3 @@ void D3D12PipelineStateViewer::on_meshView_clicked()
m_Ctx.ShowMeshPreview();
ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget());
}
void D3D12PipelineStateViewer::on_computeDebugSelector_clicked()
{
// Check whether debugging is valid for this event before showing the dialog
if(!m_Ctx.APIProps().shaderDebugging)
return;
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 D3D12PipelineStateViewer::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,7 +34,6 @@ class D3D12PipelineStateViewer;
class QXmlStreamWriter;
class ComputeDebugSelector;
class RDLabel;
class RDTreeWidget;
class RDTreeWidgetItem;
@@ -81,15 +80,10 @@ private slots:
void cbuffer_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::D3D12PipelineStateViewer *ui;
ICaptureContext &m_Ctx;
PipelineStateViewer &m_Common;
ComputeDebugSelector *m_ComputeDebugSelector;
void setOldMeshPipeFlow();
void setNewMeshPipeFlow();
@@ -3126,6 +3126,23 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pixelDebugSelector">
<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="psShaderSpacer">
<property name="orientation">
@@ -29,7 +29,6 @@
#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"
@@ -87,8 +86,6 @@ 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();
@@ -210,8 +207,10 @@ GLPipelineStateViewer::GLPipelineStateViewer(ICaptureContext &ctx, PipelineState
QObject::connect(res, &RDTreeWidget::itemActivated, this,
&GLPipelineStateViewer::resource_itemActivated);
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&GLPipelineStateViewer::computeDebugSelector_beginDebug);
QObject::connect(ui->computeDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::computeDebugSelector_clicked);
QObject::connect(ui->pixelDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::pixelDebugSelector_clicked);
{
QMenu *extensionsMenu = new QMenu(this);
@@ -1281,6 +1280,7 @@ void GLPipelineStateViewer::clearState()
ui->stencils->clear();
ui->computeDebugSelector->setEnabled(false);
ui->pixelDebugSelector->setEnabled(false);
}
void GLPipelineStateViewer::setShaderState(const GLPipe::Shader &stage, RDLabel *pipeline,
@@ -2599,56 +2599,7 @@ 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."));
}
m_Common.SetupDebugSelectors(ui->computeDebugSelector, ui->pixelDebugSelector);
// highlight the appropriate stages in the flowchart
if(action == NULL)
@@ -4001,93 +3952,3 @@ 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,7 +34,6 @@ class GLPipelineStateViewer;
class QXmlStreamWriter;
class ComputeDebugSelector;
class RDLabel;
class RDTreeWidget;
class RDTreeWidgetItem;
@@ -84,15 +83,10 @@ 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);
@@ -3720,6 +3720,23 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pixelDebugSelector">
<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="fsShaderSpacer">
<property name="orientation">
@@ -33,8 +33,10 @@
#include <QXmlStreamWriter>
#include "Code/QRDUtils.h"
#include "Code/Resources.h"
#include "Widgets/ComputeDebugSelector.h"
#include "Widgets/Extended/RDLabel.h"
#include "Widgets/Extended/RDTreeWidget.h"
#include "Widgets/PixelDebugSelector.h"
#include "toolwindowmanager/ToolWindowManager.h"
#include "D3D11PipelineStateViewer.h"
#include "D3D12PipelineStateViewer.h"
@@ -185,6 +187,15 @@ PipelineStateViewer::PipelineStateViewer(ICaptureContext &ctx, QWidget *parent)
{
ui->setupUi(this);
m_ComputeDebugSelector = new ComputeDebugSelector(this);
m_PixelDebugSelector = new PixelDebugSelector(m_Ctx, this);
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&PipelineStateViewer::computeDebugSelector_beginDebug);
QObject::connect(m_PixelDebugSelector, &PixelDebugSelector::beginDebug, this,
&PipelineStateViewer::pixelDebugSelector_beginDebug);
ui->thumbnail->SetContext(m_Ctx);
ui->layout->removeWidget(ui->thumbnail);
@@ -218,6 +229,9 @@ PipelineStateViewer::~PipelineStateViewer()
m_Ctx.BuiltinWindowClosed(this);
m_Ctx.RemoveCaptureViewer(this);
delete m_ComputeDebugSelector;
delete m_PixelDebugSelector;
delete m_Tooltip;
delete ui;
@@ -948,6 +962,259 @@ void PipelineStateViewer::shaderEdit_clicked()
menu->actions()[0]->trigger();
}
void PipelineStateViewer::SetupDebugSelectors(QToolButton *computeDebugSelector,
QToolButton *pixelDebugSelector)
{
const ShaderReflection *csRefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute);
const ShaderReflection *psRefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel);
const ActionDescription *action = m_Ctx.CurAction();
// set up thread debugging inputs
bool enableComputeDebug = m_Ctx.APIProps().shaderDebugging && csRefl &&
csRefl->debugInfo.debuggable && action &&
(action->flags & ActionFlags::Dispatch);
if(enableComputeDebug)
{
// Validate dispatch/threadgroup dimensions
enableComputeDebug &= action->dispatchDimension[0] > 0;
enableComputeDebug &= action->dispatchDimension[1] > 0;
enableComputeDebug &= action->dispatchDimension[2] > 0;
const rdcfixedarray<uint32_t, 3> &threadDims = (action->dispatchThreadsDimension[0] == 0)
? csRefl->dispatchThreadsDimension
: action->dispatchThreadsDimension;
enableComputeDebug &= threadDims[0] > 0;
enableComputeDebug &= threadDims[1] > 0;
enableComputeDebug &= threadDims[2] > 0;
}
if(enableComputeDebug)
{
computeDebugSelector->setEnabled(true);
// set maximums for CS debugging
m_ComputeDebugSelector->SetThreadBounds(action->dispatchDimension,
(action->dispatchThreadsDimension[0] == 0)
? csRefl->dispatchThreadsDimension
: action->dispatchThreadsDimension);
computeDebugSelector->setToolTip(
tr("Debug this compute shader by specifying group/thread ID or dispatch ID"));
}
else
{
computeDebugSelector->setEnabled(false);
if(!m_Ctx.APIProps().shaderDebugging)
computeDebugSelector->setToolTip(tr("This API does not support shader debugging"));
else if(!action || !(action->flags & ActionFlags::Dispatch))
computeDebugSelector->setToolTip(tr("No dispatch selected"));
else if(!csRefl)
computeDebugSelector->setToolTip(tr("No compute shader bound"));
else if(!csRefl->debugInfo.debuggable)
computeDebugSelector->setToolTip(
tr("This shader doesn't support debugging: %1").arg(csRefl->debugInfo.debugStatus));
else
computeDebugSelector->setToolTip(tr("Invalid dispatch/threadgroup dimensions."));
}
// set up thread debugging inputs
bool enablePixelDebug = m_Ctx.APIProps().shaderDebugging && psRefl &&
psRefl->debugInfo.debuggable && action &&
(action->flags & ActionFlags::Drawcall);
if(enablePixelDebug)
{
pixelDebugSelector->setEnabled(true);
pixelDebugSelector->setToolTip(
tr("Debug this pixel shader by manually selecting a co-ordinate"));
}
else
{
pixelDebugSelector->setEnabled(false);
if(!m_Ctx.APIProps().shaderDebugging)
pixelDebugSelector->setToolTip(tr("This API does not support shader debugging"));
else if(!action || !(action->flags & ActionFlags::Drawcall))
pixelDebugSelector->setToolTip(tr("No draw selected"));
else if(!psRefl)
pixelDebugSelector->setToolTip(tr("No pixel shader bound"));
else if(!psRefl->debugInfo.debuggable)
pixelDebugSelector->setToolTip(
tr("This shader doesn't support debugging: %1").arg(psRefl->debugInfo.debugStatus));
}
}
void PipelineStateViewer::computeDebugSelector_clicked()
{
// Check whether debugging is valid for this event before showing the dialog
if(!m_Ctx.APIProps().shaderDebugging)
return;
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 PipelineStateViewer::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);
}
void PipelineStateViewer::pixelDebugSelector_clicked()
{
// Check whether debugging is valid for this event before showing the dialog
if(!m_Ctx.APIProps().shaderDebugging)
return;
if(!m_Ctx.IsCaptureLoaded())
return;
const ActionDescription *action = m_Ctx.CurAction();
if(!action)
return;
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel);
if(!shaderDetails)
return;
m_PixelDebugSelector->UpdateTarget();
RDDialog::show(m_PixelDebugSelector);
}
void PipelineStateViewer::pixelDebugSelector_beginDebug(uint32_t x, uint32_t y,
const DebugPixelInputs &inputs)
{
const ActionDescription *action = m_Ctx.CurAction();
if(!action)
return;
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel);
if(!shaderDetails)
return;
bool done = false;
ShaderDebugTrace *trace = NULL;
m_Ctx.Replay().AsyncInvoke([&trace, &done, x, y, &inputs](IReplayController *r) {
trace = r->DebugPixel(x, y, inputs);
if(trace->debugger == NULL)
{
r->FreeTrace(trace);
trace = NULL;
}
done = true;
});
QString debugContext = QFormatStr("Pixel %1,%2 @ %3").arg(x).arg(y).arg(action->eventId);
// 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 pixel - make sure a valid co-ordinate is selected."));
return;
}
// viewer takes ownership of the trace
IShaderViewer *s = m_Ctx.DebugShader(
shaderDetails, m_Ctx.CurPipelineState().GetGraphicsPipelineObject(), trace, debugContext);
m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this);
}
IShaderViewer *PipelineStateViewer::EditShader(ResourceId id, ShaderStage shaderType,
const rdcstr &entry, ShaderCompileFlags compileFlags,
KnownShaderTool knownTool,
@@ -36,6 +36,9 @@ class PipelineStateViewer;
class QXmlStreamWriter;
class ComputeDebugSelector;
class PixelDebugSelector;
class QToolButton;
class QMenu;
class RDLabel;
@@ -115,6 +118,8 @@ public:
void SetupResourceView(RDTreeWidget *view);
void SetupDebugSelectors(QToolButton *computeSelector, QToolButton *pixelSelector);
QString GetVBufferFormatString(uint32_t slot);
QColor GetViewDetailsColor();
@@ -134,12 +139,23 @@ public:
public slots:
void shaderEdit_clicked();
void computeDebugSelector_clicked();
void computeDebugSelector_beginDebug(const rdcfixedarray<uint32_t, 3> &group,
const rdcfixedarray<uint32_t, 3> &thread);
void pixelDebugSelector_clicked();
void pixelDebugSelector_beginDebug(uint32_t x, uint32_t y, const DebugPixelInputs &inputs);
private:
void showEvent(QShowEvent *event) override;
Ui::PipelineStateViewer *ui;
ICaptureContext &m_Ctx;
ComputeDebugSelector *m_ComputeDebugSelector;
PixelDebugSelector *m_PixelDebugSelector;
QMenu *editMenus[NumShaderStages] = {};
RDPreviewTooltip *m_Tooltip = NULL;
@@ -30,7 +30,6 @@
#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"
@@ -142,8 +141,6 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
{
ui->setupUi(this);
m_ComputeDebugSelector = new ComputeDebugSelector(this);
const QIcon &action = Icons::action();
const QIcon &action_hover = Icons::action_hover();
@@ -262,8 +259,10 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
ui->tsDescGroup->setCollapsed(true);
ui->msDescGroup->setCollapsed(true);
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&VulkanPipelineStateViewer::computeDebugSelector_beginDebug);
QObject::connect(ui->computeDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::computeDebugSelector_clicked);
QObject::connect(ui->pixelDebugSelector, &QToolButton::clicked, &m_Common,
&PipelineStateViewer::pixelDebugSelector_clicked);
for(QToolButton *b : editButtons)
QObject::connect(b, &QToolButton::clicked, &m_Common, &PipelineStateViewer::shaderEdit_clicked);
@@ -566,7 +565,6 @@ VulkanPipelineStateViewer::~VulkanPipelineStateViewer()
{
m_CombinedImageSamplers.clear();
delete ui;
delete m_ComputeDebugSelector;
}
void VulkanPipelineStateViewer::OnCaptureLoaded()
@@ -1115,6 +1113,7 @@ void VulkanPipelineStateViewer::clearState()
ui->stencils->clear();
ui->computeDebugSelector->setEnabled(false);
ui->pixelDebugSelector->setEnabled(false);
ui->conditionalRenderingGroup->setVisible(false);
ui->csConditionalRenderingGroup->setVisible(false);
@@ -3176,56 +3175,7 @@ void VulkanPipelineStateViewer::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."));
}
m_Common.SetupDebugSelectors(ui->computeDebugSelector, ui->pixelDebugSelector);
}
void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column)
@@ -5555,96 +5505,3 @@ void VulkanPipelineStateViewer::on_meshView_clicked()
m_Ctx.ShowMeshPreview();
ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget());
}
void VulkanPipelineStateViewer::on_computeDebugSelector_clicked()
{
// Check whether debugging is valid for this event before showing the dialog
if(!m_Ctx.APIProps().shaderDebugging)
return;
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 VulkanPipelineStateViewer::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);
}
@@ -35,7 +35,6 @@ class VulkanPipelineStateViewer;
class QXmlStreamWriter;
class ComputeDebugSelector;
class RDLabel;
class RDTreeWidget;
class RDTreeWidgetItem;
@@ -89,10 +88,6 @@ private slots:
void descSet_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);
void exportHTML_clicked();
void exportFOZ_clicked();
@@ -100,7 +95,6 @@ private:
Ui::VulkanPipelineStateViewer *ui;
ICaptureContext &m_Ctx;
PipelineStateViewer &m_Common;
ComputeDebugSelector *m_ComputeDebugSelector;
void setOldMeshPipeFlow();
void setNewMeshPipeFlow();
@@ -3486,6 +3486,23 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pixelDebugSelector">
<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="fsShaderSpacer">
<property name="orientation">
+16 -1
View File
@@ -402,18 +402,33 @@ void TextureViewer::UI_UpdateCachedTexture()
return;
}
bool debugOutput = false;
ResourceId id = m_LockedId;
if(id == ResourceId())
{
debugOutput = m_Following.Type == FollowType::OutputColor ||
m_Following.Type == FollowType::OutputDepth ||
m_Following.Type == FollowType::OutputDepthResolve;
id = m_Following.GetResourceId(m_Ctx);
}
if(id == ResourceId())
{
debugOutput = false;
id = m_TexDisplay.resourceId;
}
m_CachedTexture = m_Ctx.GetTexture(id);
if(m_CachedTexture != NULL)
{
if(m_Ctx.APIProps().shaderDebugging)
if(!debugOutput)
{
ui->debugPixelContext->setEnabled(false);
ui->debugPixelContext->setToolTip(tr("No output texture selected"));
}
else if(m_Ctx.APIProps().shaderDebugging)
{
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel);
+3
View File
@@ -205,6 +205,7 @@ SOURCES += Code/qrenderdoc.cpp \
Widgets/Extended/RDDoubleSpinBox.cpp \
Widgets/Extended/RDListView.cpp \
Widgets/ComputeDebugSelector.cpp \
Widgets/PixelDebugSelector.cpp \
Widgets/CustomPaintWidget.cpp \
Widgets/ResourcePreview.cpp \
Widgets/ThumbnailStrip.cpp \
@@ -296,6 +297,7 @@ HEADERS += Code/CaptureContext.h \
Widgets/Extended/RDDoubleSpinBox.h \
Widgets/Extended/RDListView.h \
Widgets/ComputeDebugSelector.h \
Widgets/PixelDebugSelector.h \
Widgets/CustomPaintWidget.h \
Widgets/ResourcePreview.h \
Widgets/ThumbnailStrip.h \
@@ -369,6 +371,7 @@ FORMS += Windows/Dialogs/AboutDialog.ui \
Windows/PipelineState/GLPipelineStateViewer.ui \
Widgets/BufferFormatSpecifier.ui \
Widgets/ComputeDebugSelector.ui \
Widgets/PixelDebugSelector.ui \
Windows/BufferViewer.ui \
Windows/ShaderViewer.ui \
Windows/ShaderMessageViewer.ui \
+15
View File
@@ -610,6 +610,7 @@
<ClCompile Include="$(IntDir)generated\moc_ResourceInspector.cpp" />
<ClCompile Include="$(IntDir)generated\moc_BufferFormatSpecifier.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ComputeDebugSelector.cpp" />
<ClCompile Include="$(IntDir)generated\moc_PixelDebugSelector.cpp" />
<ClCompile Include="$(IntDir)generated\moc_FindReplace.cpp" />
<ClCompile Include="$(IntDir)generated\moc_BufferViewer.cpp" />
<ClCompile Include="$(IntDir)generated\moc_CaptureDialog.cpp" />
@@ -709,6 +710,7 @@
<ClCompile Include="Widgets\BufferFormatSpecifier.cpp" />
<ClCompile Include="Widgets\CollapseGroupBox.cpp" />
<ClCompile Include="Widgets\ComputeDebugSelector.cpp" />
<ClCompile Include="Widgets\PixelDebugSelector.cpp" />
<ClCompile Include="Widgets\FindReplace.cpp" />
<ClCompile Include="Widgets\Extended\RDListWidget.cpp" />
<ClCompile Include="Widgets\Extended\RDTableView.cpp" />
@@ -962,6 +964,7 @@
<ClInclude Include="$(IntDir)generated\ui_ResourceInspector.h" />
<ClInclude Include="$(IntDir)generated\ui_BufferFormatSpecifier.h" />
<ClInclude Include="$(IntDir)generated\ui_ComputeDebugSelector.h" />
<ClInclude Include="$(IntDir)generated\ui_PixelDebugSelector.h" />
<ClInclude Include="$(IntDir)generated\ui_FindReplace.h" />
<ClInclude Include="$(IntDir)generated\ui_BufferViewer.h" />
<ClInclude Include="$(IntDir)generated\ui_CaptureDialog.h" />
@@ -1040,6 +1043,12 @@
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\PixelDebugSelector.h">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\FindReplace.h">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
@@ -1491,6 +1500,12 @@
<Message>UIC %(Filename).ui</Message>
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\PixelDebugSelector.ui">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
<Message>UIC %(Filename).ui</Message>
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
</CustomBuild>
<CustomBuild Include="Widgets\FindReplace.ui">
<AdditionalInputs>%(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
@@ -801,6 +801,12 @@
<ClCompile Include="Code\Interface\Helpers.cpp">
<Filter>Code\Interface</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)generated\moc_PixelDebugSelector.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="Widgets\PixelDebugSelector.cpp">
<Filter>Widgets</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
@@ -1190,6 +1196,9 @@
<ClInclude Include="Code\Interface\Helpers.h">
<Filter>Code\Interface</Filter>
</ClInclude>
<ClInclude Include="$(IntDir)generated\ui_PixelDebugSelector.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Code\pyrenderdoc\pyconversion.h">
@@ -1634,6 +1643,12 @@
<CustomBuild Include="Widgets\Extended\RDToolTip.h">
<Filter>Widgets\Extended</Filter>
</CustomBuild>
<CustomBuild Include="Widgets\PixelDebugSelector.ui">
<Filter>Widgets</Filter>
</CustomBuild>
<CustomBuild Include="Widgets\PixelDebugSelector.h">
<Filter>Widgets</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="Resources\action.png">
+6
View File
@@ -723,6 +723,12 @@ struct FBO
:type: int
)");
int32_t readBuffer = 0;
DOCUMENT(R"(The default sample count to use if the framebuffer has no attachments
:type: int
)");
uint32_t defaultSampleCount = 0;
};
DOCUMENT(R"(
+6
View File
@@ -176,6 +176,12 @@ public:
)");
uint32_t MultiviewBroadcastCount() const;
DOCUMENT(R"(
:return: The number of multisample samples to use when not implicit from output targets.
:rtype: int
)");
uint32_t GetOverrideSampleCount() const;
DOCUMENT(R"(Determines whether or not the current capture supports binding arrays of resources.
:return: A boolean indicating if binding arrays of resources is supported.
+25
View File
@@ -397,6 +397,31 @@ uint32_t PipeState::MultiviewBroadcastCount() const
return 1;
}
uint32_t PipeState::GetOverrideSampleCount() const
{
if(IsCaptureLoaded())
{
if(IsCaptureD3D11())
{
return m_D3D11->rasterizer.state.forcedSampleCount;
}
else if(IsCaptureD3D12())
{
return m_D3D11->rasterizer.state.forcedSampleCount;
}
else if(IsCaptureGL())
{
return m_GL->framebuffer.drawFBO.defaultSampleCount;
}
else if(IsCaptureVK())
{
return m_Vulkan->multisample.rasterSamples;
}
}
return 0;
}
rdcstr PipeState::GetShaderEntryPoint(ShaderStage stage) const
{
if(IsCaptureLoaded() && IsCaptureVK())
+1
View File
@@ -769,6 +769,7 @@ extern bool IsGLES;
EXT_TO_CHECK(43, 31, ARB_program_interface_query) \
EXT_TO_CHECK(43, 31, ARB_shader_storage_buffer_object) \
EXT_TO_CHECK(43, 31, ARB_stencil_texturing) \
EXT_TO_CHECK(43, 31, ARB_framebuffer_no_attachments) \
/* See above with ARB_texture_multisample_no_array */ \
EXT_TO_CHECK(43, 32, ARB_texture_storage_multisample_no_array) \
EXT_TO_CHECK(43, 99, ARB_texture_storage_multisample) \
+8
View File
@@ -1884,6 +1884,14 @@ void GLReplay::SavePipelineState(uint32_t eventId)
pipe.framebuffer.drawFBO.readBuffer = b - eGL_COLOR_ATTACHMENT0;
else
pipe.framebuffer.drawFBO.readBuffer = -1;
if(HasExt[ARB_framebuffer_no_attachments])
{
drv.glGetFramebufferParameteriv(eGL_READ_FRAMEBUFFER, eGL_FRAMEBUFFER_DEFAULT_SAMPLES,
(GLint *)&pipe.framebuffer.readFBO.defaultSampleCount);
drv.glGetFramebufferParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_FRAMEBUFFER_DEFAULT_SAMPLES,
(GLint *)&pipe.framebuffer.drawFBO.defaultSampleCount);
}
}
pipe.framebuffer.blendState.blendFactor = rs.BlendColor;