diff --git a/qrenderdoc/Widgets/PixelDebugSelector.cpp b/qrenderdoc/Widgets/PixelDebugSelector.cpp new file mode 100644 index 000000000..3c2f29188 --- /dev/null +++ b/qrenderdoc/Widgets/PixelDebugSelector.cpp @@ -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 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(); +} diff --git a/qrenderdoc/Widgets/PixelDebugSelector.h b/qrenderdoc/Widgets/PixelDebugSelector.h new file mode 100644 index 000000000..036be6378 --- /dev/null +++ b/qrenderdoc/Widgets/PixelDebugSelector.h @@ -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 +#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; +}; diff --git a/qrenderdoc/Widgets/PixelDebugSelector.ui b/qrenderdoc/Widgets/PixelDebugSelector.ui new file mode 100644 index 000000000..9ead7c760 --- /dev/null +++ b/qrenderdoc/Widgets/PixelDebugSelector.ui @@ -0,0 +1,164 @@ + + + PixelDebugSelector + + + Qt::ApplicationModal + + + + 0 + 0 + 176 + 274 + + + + + 0 + 0 + + + + Debug Pixel Shader + + + false + + + true + + + + QLayout::SetFixedSize + + + + + Pixel co-ordinate + + + + 3 + + + + + X: + + + + + + + 16384 + + + + + + + Y: + + + + + + + 16384 + + + + + + + Use Selected Pixel + + + + :/wand.png:/wand.png + + + + + + + + + + Disambiguation + + + + + + Primitive: + + + + + + + + + + + + + Sample: + + + + + + + Multiview: + + + + + + + Any + + + + + + + + + + + + Debug + + + + :/wrench.png:/wrench.png + + + + + + + Cancel + + + + + + + + + + RDLineEdit + QLineEdit +
Widgets/Extended/RDLineEdit.h
+
+
+ + + + +
diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index d0497a077..1625098dc 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -29,7 +29,6 @@ #include #include #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 &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 &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/D3D11PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h index b8b5b934e..820f079d8 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h @@ -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 &group, - const rdcfixedarray &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, diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui index 3efd6cd6d..8d06b5396 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.ui @@ -3010,6 +3010,23 @@ + + + + Debug + + + + :/wrench.png:/wrench.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index f333ad92c..fa4c989bc 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -29,7 +29,6 @@ #include #include #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 &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 &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/D3D12PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h index 2500ce445..60252bede 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.h @@ -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 &group, - const rdcfixedarray &thread); - private: Ui::D3D12PipelineStateViewer *ui; ICaptureContext &m_Ctx; PipelineStateViewer &m_Common; - ComputeDebugSelector *m_ComputeDebugSelector; void setOldMeshPipeFlow(); void setNewMeshPipeFlow(); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui index ea54329d0..72898bb23 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.ui @@ -3126,6 +3126,23 @@ + + + + Debug + + + + :/wrench.png:/wrench.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index e29baedd0..03ee61e34 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -29,7 +29,6 @@ #include #include #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 &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 &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 6e7ee8e04..eae9ec1c9 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.h @@ -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 &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 06f4caa96..950635dcb 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.ui @@ -3720,6 +3720,23 @@ + + + + Debug + + + + :/wrench.png:/wrench.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 13e8484bb..8c224ee3a 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -33,8 +33,10 @@ #include #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 &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 &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); +} + +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, diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index f94366e8f..6596e770c 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -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 &group, + const rdcfixedarray &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; diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 2258cbefa..10a986019 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -30,7 +30,6 @@ #include #include #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 &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 &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/VulkanPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h index 5be6dc15a..ee6f3fbdc 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h @@ -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 &group, - const rdcfixedarray &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(); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui index 8f1433a9f..9456123f6 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui @@ -3486,6 +3486,23 @@ + + + + Debug + + + + :/wrench.png:/wrench.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 7a506113e..feb2b03ff 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -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); diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index dabf69db3..f4fe37189 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -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 \ diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index 158373bec..2dd34b34a 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -610,6 +610,7 @@ + @@ -709,6 +710,7 @@ + @@ -962,6 +964,7 @@ + @@ -1040,6 +1043,12 @@ MOC %(Filename).h $(IntDir)generated\moc_%(Filename).cpp + + %(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs) + "$(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" + MOC %(Filename).h + $(IntDir)generated\moc_%(Filename).cpp + %(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs) "$(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" @@ -1491,6 +1500,12 @@ UIC %(Filename).ui $(IntDir)generated\ui_%(Filename).h + + %(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs) + "$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h" + UIC %(Filename).ui + $(IntDir)generated\ui_%(Filename).h + %(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs) "$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h" diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index aaf333101..116638ab6 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -801,6 +801,12 @@ Code\Interface + + Generated Files + + + Widgets + @@ -1190,6 +1196,9 @@ Code\Interface + + Generated Files + @@ -1634,6 +1643,12 @@ Widgets\Extended + + Widgets + + + Widgets + diff --git a/renderdoc/api/replay/gl_pipestate.h b/renderdoc/api/replay/gl_pipestate.h index a6ed9be0a..1d43e7014 100644 --- a/renderdoc/api/replay/gl_pipestate.h +++ b/renderdoc/api/replay/gl_pipestate.h @@ -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"( diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index 9f9fd1d2a..a3864a3de 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -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. diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index a545d70e8..9fbddfdb4 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -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()) diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index d0d073b4d..792abaf12 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -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) \ diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index c77288fbb..62d41c5ae 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -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;