From 3ea9735866d32e1915512cc83391e050b7146bc3 Mon Sep 17 00:00:00 2001 From: Honeybunch Date: Tue, 19 Nov 2024 09:29:26 -0800 Subject: [PATCH] Mesh Shader Debugging --- qrenderdoc/Widgets/ComputeDebugSelector.cpp | 15 ++ qrenderdoc/Widgets/ComputeDebugSelector.h | 2 + qrenderdoc/Windows/BufferViewer.cpp | 170 ++++++++++++++++++ qrenderdoc/Windows/BufferViewer.h | 6 + qrenderdoc/Windows/ShaderMessageViewer.cpp | 4 + renderdoc/api/replay/renderdoc_replay.h | 11 ++ renderdoc/core/image_viewer.cpp | 5 + renderdoc/core/replay_proxy.cpp | 38 ++++ renderdoc/core/replay_proxy.h | 4 + renderdoc/driver/d3d11/d3d11_replay.h | 2 + renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 8 + renderdoc/driver/d3d12/d3d12_replay.h | 2 + renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 8 + renderdoc/driver/gl/gl_replay.cpp | 8 + renderdoc/driver/gl/gl_replay.h | 2 + .../driver/shaders/spirv/spirv_debug.cpp | 7 +- .../shaders/spirv/spirv_debug_setup.cpp | 9 +- renderdoc/driver/vulkan/vk_replay.h | 2 + renderdoc/driver/vulkan/vk_shaderdebug.cpp | 83 +++++++++ renderdoc/replay/dummy_driver.cpp | 7 + renderdoc/replay/dummy_driver.h | 2 + renderdoc/replay/replay_controller.cpp | 18 ++ renderdoc/replay/replay_controller.h | 2 + renderdoc/replay/replay_driver.h | 3 + 24 files changed, 410 insertions(+), 8 deletions(-) diff --git a/qrenderdoc/Widgets/ComputeDebugSelector.cpp b/qrenderdoc/Widgets/ComputeDebugSelector.cpp index b117cc8f8c..c78b4f74e1 100644 --- a/qrenderdoc/Widgets/ComputeDebugSelector.cpp +++ b/qrenderdoc/Widgets/ComputeDebugSelector.cpp @@ -65,6 +65,21 @@ ComputeDebugSelector::~ComputeDebugSelector() delete ui; } +void ComputeDebugSelector::SetDefaultDispatch(const rdcfixedarray &group, + const rdcfixedarray &thread) +{ + QSignalBlocker blockers[6] = {QSignalBlocker(ui->groupX), QSignalBlocker(ui->groupY), + QSignalBlocker(ui->groupZ), QSignalBlocker(ui->threadX), + QSignalBlocker(ui->threadY), QSignalBlocker(ui->threadZ)}; + + ui->groupX->setValue(group[0]); + ui->groupY->setValue(group[1]); + ui->groupZ->setValue(group[2]); + ui->threadX->setValue(thread[0]); + ui->threadY->setValue(thread[1]); + ui->threadZ->setValue(thread[2]); +} + void ComputeDebugSelector::SetThreadBounds(const rdcfixedarray &group, const rdcfixedarray &thread) { diff --git a/qrenderdoc/Widgets/ComputeDebugSelector.h b/qrenderdoc/Widgets/ComputeDebugSelector.h index 2657869e2b..93c0ff63b3 100644 --- a/qrenderdoc/Widgets/ComputeDebugSelector.h +++ b/qrenderdoc/Widgets/ComputeDebugSelector.h @@ -40,6 +40,8 @@ class ComputeDebugSelector : public QDialog explicit ComputeDebugSelector(QWidget *parent = 0); ~ComputeDebugSelector(); + void SetDefaultDispatch(const rdcfixedarray &group, + const rdcfixedarray &thread); void SetThreadBounds(const rdcfixedarray &group, const rdcfixedarray &thread); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 874cab7f63..040aaa9d7f 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -39,6 +39,7 @@ #include "Code/QRDUtils.h" #include "Code/Resources.h" #include "Widgets/CollapseGroupBox.h" +#include "Widgets/ComputeDebugSelector.h" #include "Widgets/Extended/RDLabel.h" #include "Widgets/Extended/RDSplitter.h" #include "Windows/Dialogs/AxisMappingDialog.h" @@ -2361,6 +2362,8 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) m_ModelOut1 = new BufferItemModel(ui->out1Table, false, meshview, this); m_ModelOut2 = new BufferItemModel(ui->out2Table, false, meshview, this); + m_MeshDebugSelector = new ComputeDebugSelector(this); + // we keep the old UI names for serialised layouts compatibility QString containerNames[] = { lit("vsinData"), @@ -2440,6 +2443,9 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) m_DebugVert = new QAction(tr("&Debug this Vertex"), this); m_DebugVert->setIcon(Icons::wrench()); + m_DebugMeshThread = new QAction(tr("&Debug Mesh Thread"), this); + m_DebugMeshThread->setIcon(Icons::wrench()); + m_FilterMesh = new QAction(tr("&Filter to this Meshlet"), this); m_FilterMesh->setIcon(Icons::filter()); @@ -2458,6 +2464,7 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) QObject::connect(m_ExportBytes, &QAction::triggered, [this] { exportData(BufferExport(BufferExport::RawBytes)); }); QObject::connect(m_DebugVert, &QAction::triggered, this, &BufferViewer::debugVertex); + QObject::connect(m_DebugMeshThread, &QAction::triggered, this, &BufferViewer::debugMeshThread); QObject::connect(m_RemoveFilter, &QAction::triggered, [this]() { SetMeshFilter(MeshFilter::None); }); QObject::connect(m_FilterMesh, &QAction::triggered, [this]() { @@ -2624,6 +2631,9 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) ui->fixedVars->setTooltipElidedItems(false); ui->fixedVars->installEventFilter(this); + QObject::connect(m_MeshDebugSelector, &ComputeDebugSelector::beginDebug, this, + &BufferViewer::meshDebugSelector_beginDebug); + Reset(); m_Ctx.AddCaptureViewer(this); @@ -3093,6 +3103,37 @@ void BufferViewer::stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint & menu->addAction(m_RemoveFilter); menu->addAction(m_FilterMesh); menu->addAction(m_GotoTask); + + const ShaderReflection *shaderDetails = + m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); + + m_DebugMeshThread->setEnabled(false); + + if(!m_Ctx.APIProps().shaderDebugging) + { + m_DebugMeshThread->setToolTip(tr("This API does not support shader debugging")); + } + else if(!m_Ctx.CurAction() || + !(m_Ctx.CurAction()->flags & (ActionFlags::Drawcall | ActionFlags::MeshDispatch))) + { + m_DebugMeshThread->setToolTip(tr("No draw call selected")); + } + else if(!shaderDetails) + { + m_DebugMeshThread->setToolTip(tr("No mesh shader bound")); + } + else if(!shaderDetails->debugInfo.debuggable) + { + m_DebugMeshThread->setToolTip( + tr("This shader doesn't support debugging: %1").arg(shaderDetails->debugInfo.debugStatus)); + } + else + { + m_DebugMeshThread->setEnabled(true); + m_DebugMeshThread->setToolTip(QString()); + } + menu->addAction(m_DebugMeshThread); + menu->addSeparator(); m_GotoTask->setEnabled(m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Task)); @@ -6638,6 +6679,135 @@ void BufferViewer::debugVertex() m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } +void BufferViewer::debugMeshThread() +{ + if(!m_Ctx.IsCaptureLoaded()) + return; + + const ActionDescription *action = m_Ctx.CurAction(); + if(!action) + return; + + if(!m_CurView) + return; + + QModelIndex idx = m_CurView->selectionModel()->currentIndex(); + + if(!idx.isValid()) + { + GUIInvoke::call(this, [this]() { + RDDialog::critical(this, tr("Error debugging"), + tr("Error debugging meshlet - make sure a valid meshlet is selected")); + }); + return; + } + + uint32_t taskIndex = 0, meshletIndex = 0; + GetIndicesForMeshRow((uint32_t)idx.row(), taskIndex, meshletIndex); + + const ShaderReflection *shaderDetails = + m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); + + if(!shaderDetails) + return; + + rdcfixedarray threadGroupSize = action->dispatchThreadsDimension[0] == 0 + ? shaderDetails->dispatchThreadsDimension + : action->dispatchThreadsDimension; + m_MeshDebugSelector->SetThreadBounds(action->dispatchDimension, threadGroupSize); + + // Calculate 3d group id from 1d meshlet index and dispatch dimensions + // Imagine 8x2x4 with idx 60 + // 8x2 = 16 + // 8x2x4 = 64 + // 60 % x = 4 + // 60 % (x * y) = 12 / x = 1 + // 60 / (x * y) = 3 + // index 60 is id (4,1,3) + // 4 + (8 * 1) + (16 * 3) = 60 + uint32_t xDim = action->dispatchDimension[0]; + uint32_t yDim = action->dispatchDimension[1]; + uint32_t zDim = action->dispatchDimension[2]; + rdcfixedarray meshletGroup = { + meshletIndex % xDim, + (meshletIndex % (xDim * yDim)) / xDim, + meshletIndex / (xDim * yDim), + }; + m_MeshDebugSelector->SetDefaultDispatch(meshletGroup, {0, 0, 0}); + + RDDialog::show(m_MeshDebugSelector); +} + +void BufferViewer::meshDebugSelector_beginDebug(const rdcfixedarray &group, + const rdcfixedarray &thread) +{ + const ActionDescription *action = m_Ctx.CurAction(); + + if(!action) + return; + + const ShaderReflection *shaderDetails = + m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Mesh); + + 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->DebugMeshThread(debugThread.g, debugThread.t); + + if(trace->debugger == NULL) + { + r->FreeTrace(trace); + trace = NULL; + } + + done = true; + }); + + QString debugContext = lit("Mesh 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 BufferViewer::SyncViews(RDTableView *primary, bool selection, bool scroll) { if(!ui->syncViews->isChecked()) diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index db6dffbfd7..318995764e 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -34,6 +34,7 @@ namespace Ui class BufferViewer; } +class ComputeDebugSelector; class RDSpinBox64; class QItemSelection; class QMenu; @@ -164,12 +165,16 @@ private slots: void updateExportActionNames(); void exportData(const BufferExport ¶ms); void debugVertex(); + void debugMeshThread(); + void meshDebugSelector_beginDebug(const rdcfixedarray &group, + const rdcfixedarray &thread); void fixedVars_contextMenu(const QPoint &pos); private: bool eventFilter(QObject *watched, QEvent *event) override; Ui::BufferViewer *ui; ICaptureContext &m_Ctx; + ComputeDebugSelector *m_MeshDebugSelector; IReplayOutput *m_Output; @@ -323,6 +328,7 @@ private slots: QAction *m_ExportCSV = NULL; QAction *m_ExportBytes = NULL; QAction *m_DebugVert = NULL; + QAction *m_DebugMeshThread = NULL; QAction *m_FilterMesh = NULL; QAction *m_RemoveFilter = NULL; QAction *m_GotoTask = NULL; diff --git a/qrenderdoc/Windows/ShaderMessageViewer.cpp b/qrenderdoc/Windows/ShaderMessageViewer.cpp index fcee090e77..03d48a6288 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.cpp +++ b/qrenderdoc/Windows/ShaderMessageViewer.cpp @@ -266,6 +266,10 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s inputs.view = msg.location.pixel.view; trace = r->DebugPixel(msg.location.pixel.x, msg.location.pixel.y, inputs); } + else if(msg.stage == ShaderStage::Mesh) + { + trace = r->DebugMeshThread(msg.location.mesh.meshGroup, msg.location.mesh.thread); + } if(trace && trace->debugger == NULL) { diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 7b0f854504..dcfe556d21 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1021,6 +1021,17 @@ bucket when the pixel values are divided between ``minval`` and ``maxval``. virtual ShaderDebugTrace *DebugThread(const rdcfixedarray &groupid, const rdcfixedarray &threadid) = 0; + DOCUMENT(R"(Retrieve a debugging trace from running a mesh shader. + +:param Tuple[int,int,int] groupid: A list containing the 3D workgroup index. +:param Tuple[int,int,int] threadid: A list containing the 3D thread index within the workgroup. +:return: The resulting trace resulting from debugging. Destroy with + :meth:`FreeTrace`. +:rtype: ShaderDebugTrace +)"); + virtual ShaderDebugTrace *DebugMeshThread(const rdcfixedarray &groupid, + const rdcfixedarray &threadid) = 0; + DOCUMENT(R"(Continue a shader's debugging with a given shader debugger instance. This will run an implementation defined number of steps and then return those steps in a list. This may be a fixed number of steps or it may run for a fixed length of time and return as many steps as can be diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 1a8d7422d5..bd6735e78c 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -357,6 +357,11 @@ class ImageViewer : public IReplayDriver { return new ShaderDebugTrace(); } + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid) + { + return new ShaderDebugTrace(); + } rdcarray ContinueDebug(ShaderDebugger *debugger) { return {}; } void FreeDebugger(ShaderDebugger *debugger) { delete debugger; } void BuildTargetShader(ShaderEncoding sourceEncoding, const bytebuf &source, const rdcstr &entry, diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index bd7927bccd..da72fb7fae 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1684,6 +1684,44 @@ ShaderDebugTrace *ReplayProxy::DebugThread(uint32_t eventId, PROXY_FUNCTION(DebugThread, eventId, groupid, threadid); } +template +ShaderDebugTrace *ReplayProxy::Proxied_DebugMeshThread(ParamSerialiser ¶mser, + ReturnSerialiser &retser, uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + const ReplayProxyPacket expectedPacket = eReplayProxy_DebugMeshThread; + ReplayProxyPacket packet = eReplayProxy_DebugMeshThread; + ShaderDebugTrace *ret; + + { + BEGIN_PARAMS(); + SERIALISE_ELEMENT(eventId); + SERIALISE_ELEMENT(groupid); + SERIALISE_ELEMENT(threadid); + END_PARAMS(); + } + + { + REMOTE_EXECUTION(); + if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) + ret = m_Remote->DebugMeshThread(eventId, groupid, threadid); + else + ret = new ShaderDebugTrace; + } + + SERIALISE_RETURN(*ret); + + return ret; +} + +ShaderDebugTrace *ReplayProxy::DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + PROXY_FUNCTION(DebugMeshThread, eventId, groupid, threadid); +} + template rdcarray ReplayProxy::Proxied_ContinueDebug(ParamSerialiser ¶mser, ReturnSerialiser &retser, diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 014227e8ad..7f47258680 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -89,6 +89,7 @@ enum ReplayProxyPacket eReplayProxy_DebugVertex, eReplayProxy_DebugPixel, eReplayProxy_DebugThread, + eReplayProxy_DebugMeshThread, eReplayProxy_RenderOverlay, @@ -552,6 +553,9 @@ class ReplayProxy : public IReplayDriver IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace *, DebugThread, uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace *, DebugMeshThread, uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid); IMPLEMENT_FUNCTION_PROXIED(rdcarray, ContinueDebug, ShaderDebugger *debugger); IMPLEMENT_FUNCTION_PROXIED(void, FreeDebugger, ShaderDebugger *debugger); diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index dbb1f4058a..89fe1d7845 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -288,6 +288,8 @@ class D3D11Replay : public IReplayDriver const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeDebugger(ShaderDebugger *debugger); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 65a50c8629..e7d1978b30 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -2688,6 +2688,14 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId, return ret; } +ShaderDebugTrace *D3D11Replay::DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + // Not supported + return new ShaderDebugTrace; +} + rdcarray D3D11Replay::ContinueDebug(ShaderDebugger *debugger) { DXBCDebug::InterpretDebugger *interpreter = (DXBCDebug::InterpretDebugger *)debugger; diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 51175dfaa2..2d001a2154 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -251,6 +251,8 @@ class D3D12Replay : public IReplayDriver const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeDebugger(ShaderDebugger *debugger); diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 2a4a4bb9f1..e5f7c3efdb 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -3377,6 +3377,14 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, return ret; } +ShaderDebugTrace *D3D12Replay::DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + // Not implemented yet + return new ShaderDebugTrace; +} + rdcarray D3D12Replay::ContinueDebug(ShaderDebugger *debugger) { if(!debugger) diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 2c34e6d6a8..21fbd47519 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -4300,6 +4300,14 @@ ShaderDebugTrace *GLReplay::DebugThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + GLNOTIMP("DebugMeshThread"); + return new ShaderDebugTrace(); +} + rdcarray GLReplay::ContinueDebug(ShaderDebugger *debugger) { GLNOTIMP("ContinueDebug"); diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 152f39f406..83ecf7b040 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -260,6 +260,8 @@ class GLReplay : public IReplayDriver const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeDebugger(ShaderDebugger *debugger); uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg, diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.cpp b/renderdoc/driver/shaders/spirv/spirv_debug.cpp index f446e53f08..c34667d4c4 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug.cpp @@ -3521,6 +3521,12 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray break; } + case Op::SetMeshOutputsEXT: + { + // Ignore mesh outputs, nothing to do, really + break; + } + // TODO sparse sampling case Op::ImageSparseSampleImplicitLod: case Op::ImageSparseSampleExplicitLod: @@ -3750,7 +3756,6 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray case Op::ConvertSampledImageToUNV: case Op::SamplerImageAddressingModeNV: case Op::EmitMeshTasksEXT: - case Op::SetMeshOutputsEXT: case Op::HitObjectRecordHitMotionNV: case Op::HitObjectRecordHitWithIndexMotionNV: case Op::HitObjectRecordMissMotionNV: diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index 83b297d277..4230c373e5 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -437,6 +437,7 @@ void Reflector::CheckDebuggable(bool &debuggable, rdcstr &debugStatus) const "SPV_GOOGLE_user_type", "SPV_KHR_physical_storage_buffer", "SPV_KHR_relaxed_extended_instruction", + "SPV_EXT_mesh_shader", }; // whitelist supported extensions @@ -554,6 +555,7 @@ void Reflector::CheckDebuggable(bool &debuggable, rdcstr &debugStatus) const case Capability::UniformDecoration: case Capability::SignedZeroInfNanPreserve: case Capability::PhysicalStorageBufferAddresses: + case Capability::MeshShadingEXT: { supported = true; break; @@ -646,13 +648,6 @@ void Reflector::CheckDebuggable(bool &debuggable, rdcstr &debugStatus) const break; } - // mesh shading - case Capability::MeshShadingEXT: - { - supported = false; - break; - } - // no plans to support these - mostly Kernel/OpenCL related or vendor extensions case Capability::Addresses: case Capability::Linkage: diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 1433da516b..10c67fa016 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -451,6 +451,8 @@ class VulkanReplay : public IReplayDriver const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeDebugger(ShaderDebugger *debugger); diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index 01257a2696..4090bbe453 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -5080,6 +5080,89 @@ ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, return ret; } +ShaderDebugTrace *VulkanReplay::DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + const VulkanRenderState &state = m_pDriver->GetRenderState(); + VulkanCreationInfo &c = m_pDriver->m_CreationInfo; + + rdcstr regionName = + StringFormat::Fmt("DebugMeshThread @ %u of (%u,%u,%u) (%u,%u,%u)", eventId, groupid[0], + groupid[1], groupid[2], threadid[0], threadid[1], threadid[2]); + + VkMarkerRegion region(regionName); + + if(Vulkan_Debug_ShaderDebugLogging()) + RDCLOG("%s", regionName.c_str()); + + const ActionDescription *action = m_pDriver->GetAction(eventId); + + if(!(action->flags & ActionFlags::MeshDispatch)) + { + RDCLOG("No mesh dispatch selected"); + return new ShaderDebugTrace(); + } + + // get ourselves in pristine state before this dispatch (without any side effects it may have had) + m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw); + + const VulkanCreationInfo::Pipeline &pipe = c.m_Pipeline[state.graphics.pipeline]; + const VulkanCreationInfo::ShaderEntry &shaderEntry = + state.graphics.shaderObject + ? c.m_ShaderObject[state.shaderObjects[(size_t)ShaderStage::Mesh]].shad + : pipe.shaders[(size_t)ShaderStage::Mesh]; + VulkanCreationInfo::ShaderModule &shader = c.m_ShaderModule[shaderEntry.module]; + rdcstr entryPoint = shaderEntry.entryPoint; + const rdcarray &spec = shaderEntry.specialization; + + VulkanCreationInfo::ShaderModuleReflection &shadRefl = + shader.GetReflection(ShaderStage::Mesh, entryPoint, state.graphics.pipeline); + + if(!shadRefl.refl->debugInfo.debuggable) + { + RDCLOG("Shader is not debuggable: %s", shadRefl.refl->debugInfo.debugStatus.c_str()); + return new ShaderDebugTrace(); + } + + shadRefl.PopulateDisassembly(shader.spirv); + + VulkanAPIWrapper *apiWrapper = + new VulkanAPIWrapper(m_pDriver, c, ShaderStage::Mesh, eventId, shadRefl.refl->resourceId); + + uint32_t threadDim[3]; + threadDim[0] = shadRefl.refl->dispatchThreadsDimension[0]; + threadDim[1] = shadRefl.refl->dispatchThreadsDimension[1]; + threadDim[2] = shadRefl.refl->dispatchThreadsDimension[2]; + + std::map &builtins = apiWrapper->builtin_inputs; + builtins[ShaderBuiltin::DispatchSize] = + ShaderVariable(rdcstr(), action->dispatchDimension[0], action->dispatchDimension[1], + action->dispatchDimension[2], 0U); + builtins[ShaderBuiltin::DispatchThreadIndex] = ShaderVariable( + rdcstr(), groupid[0] * threadDim[0] + threadid[0], groupid[1] * threadDim[1] + threadid[1], + groupid[2] * threadDim[2] + threadid[2], 0U); + builtins[ShaderBuiltin::GroupIndex] = + ShaderVariable(rdcstr(), groupid[0], groupid[1], groupid[2], 0U); + builtins[ShaderBuiltin::GroupSize] = + ShaderVariable(rdcstr(), threadDim[0], threadDim[1], threadDim[2], 0U); + builtins[ShaderBuiltin::GroupThreadIndex] = + ShaderVariable(rdcstr(), threadid[0], threadid[1], threadid[2], 0U); + builtins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable( + rdcstr(), threadid[2] * threadDim[0] * threadDim[1] + threadid[1] * threadDim[0] + threadid[0], + 0U, 0U, 0U); + builtins[ShaderBuiltin::DeviceIndex] = ShaderVariable(rdcstr(), 0U, 0U, 0U, 0U); + builtins[ShaderBuiltin::DrawIndex] = ShaderVariable(rdcstr(), action->drawIndex, 0U, 0U, 0U); + + rdcspv::Debugger *debugger = new rdcspv::Debugger; + debugger->Parse(shader.spirv.GetSPIRV()); + ShaderDebugTrace *ret = debugger->BeginDebug(apiWrapper, ShaderStage::Mesh, entryPoint, spec, + shadRefl.instructionLines, shadRefl.patchData, 0); + apiWrapper->ResetReplay(); + + return ret; +} + rdcarray VulkanReplay::ContinueDebug(ShaderDebugger *debugger) { rdcspv::Debugger *spvDebugger = (rdcspv::Debugger *)debugger; diff --git a/renderdoc/replay/dummy_driver.cpp b/renderdoc/replay/dummy_driver.cpp index d6202ad893..47a361d84a 100644 --- a/renderdoc/replay/dummy_driver.cpp +++ b/renderdoc/replay/dummy_driver.cpp @@ -296,6 +296,13 @@ ShaderDebugTrace *DummyDriver::DebugVertex(uint32_t eventId, uint32_t vertid, ui return new ShaderDebugTrace; } +ShaderDebugTrace *DummyDriver::DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + return new ShaderDebugTrace; +} + ShaderDebugTrace *DummyDriver::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, const DebugPixelInputs &inputs) { diff --git a/renderdoc/replay/dummy_driver.h b/renderdoc/replay/dummy_driver.h index 2ec6c3b1bd..c8b4159d64 100644 --- a/renderdoc/replay/dummy_driver.h +++ b/renderdoc/replay/dummy_driver.h @@ -114,6 +114,8 @@ class DummyDriver : public IReplayDriver const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(uint32_t eventId, const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeDebugger(ShaderDebugger *debugger); diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 4f7b3a3a97..0830a74c63 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -1698,6 +1698,24 @@ ShaderDebugTrace *ReplayController::DebugThread(const rdcfixedarray return ret; } +ShaderDebugTrace *ReplayController::DebugMeshThread(const rdcfixedarray &groupid, + const rdcfixedarray &threadid) +{ + CHECK_REPLAY_THREAD(); + + RENDERDOC_PROFILEFUNCTION(); + + ShaderDebugTrace *ret = m_pDevice->DebugMeshThread(m_EventID, groupid, threadid); + FatalErrorCheck(); + + SetFrameEvent(m_EventID, true); + + if(ret->debugger) + m_Debuggers.push_back(ret->debugger); + + return ret; +} + rdcarray ReplayController::ContinueDebug(ShaderDebugger *debugger) { CHECK_REPLAY_THREAD(); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index fbd15b4681..6a55f0662d 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -214,6 +214,8 @@ struct ReplayController : public IReplayController ShaderDebugTrace *DebugPixel(uint32_t x, uint32_t y, const DebugPixelInputs &inputs); ShaderDebugTrace *DebugThread(const rdcfixedarray &groupid, const rdcfixedarray &threadid); + ShaderDebugTrace *DebugMeshThread(const rdcfixedarray &groupid, + const rdcfixedarray &threadid); rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeTrace(ShaderDebugTrace *trace); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 1e7b7f7fab..eaf901357e 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -231,6 +231,9 @@ class IRemoteDriver const DebugPixelInputs &inputs) = 0; virtual ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray &groupid, const rdcfixedarray &threadid) = 0; + virtual ShaderDebugTrace *DebugMeshThread(uint32_t eventId, + const rdcfixedarray &groupid, + const rdcfixedarray &threadid) = 0; virtual rdcarray ContinueDebug(ShaderDebugger *debugger) = 0; virtual void FreeDebugger(ShaderDebugger *debugger) = 0;