diff --git a/qrenderdoc/Widgets/ComputeDebugSelector.cpp b/qrenderdoc/Widgets/ComputeDebugSelector.cpp index b117cc8f8ca..c78b4f74e14 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 2657869e2b9..93c0ff63b30 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 874cab7f631..119c0d32f6c 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,8 @@ 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 +2632,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 +3104,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 +6680,133 @@ 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 db6dffbfd7c..dc0aef81038 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 fcee090e770..03d48a62887 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 7b0f854504d..88408fbad9f 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 1a8d7422d5f..9c0fdb0324b 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 bd7927bccde..bfd06441cff 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 014227e8ad0..7f472586804 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 dbb1f4058a5..d200c937aed 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 65a50c8629d..e3e44213260 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 51175dfaa20..6c13a9b0878 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 2a4a4bb9f1b..9f73cce21a8 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 2c34e6d6a8c..fe4818fc353 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 152f39f4066..56ffdde52c5 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 f446e53f082..c34667d4c4a 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 83b297d277f..4230c373e55 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 1433da516b5..e2c3d9f8d4f 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 01257a26967..73ecb433ac4 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -1526,7 +1526,15 @@ class VulkanAPIWrapper : public rdcspv::DebugAPIWrapper } }; + struct TexelBufData + { + uint32_t texelSize = 0; + ResourceFormat fmt; + bytebuf bytes; + }; + std::map imageCache; + std::map texelBufCache; const Descriptor &GetDescriptor(const rdcstr &access, ShaderBindIndex index, bool &valid) { @@ -1766,6 +1774,43 @@ class VulkanAPIWrapper : public rdcspv::DebugAPIWrapper return data; } + TexelBufData &PopulateTexelBuffer(ShaderBindIndex bind) + { + auto insertIt = texelBufCache.insert(std::make_pair(bind, TexelBufData())); + TexelBufData &data = insertIt.first->second; + if(insertIt.second) + { + bool valid = true; + const Descriptor &bufData = GetDescriptor("accessing texel buffer value", bind, valid); + if(valid) + { + // if the resources might be dirty from side-effects from the action, replay back to right + // before it. + if(m_ResourcesDirty) + { + VkMarkerRegion region("un-dirtying resources"); + m_pDriver->ReplayLog(0, m_EventID, eReplay_WithoutDraw); + m_ResourcesDirty = false; + } + + if(bufData.resource != ResourceId()) + { + const VulkanCreationInfo::BufferView &viewProps = + m_Creation.m_BufferView[m_pDriver->GetResourceManager()->GetLiveID(bufData.view)]; + + data.fmt = MakeResourceFormat(viewProps.format); + data.texelSize = (uint32_t)GetByteSize(1, 1, 1, viewProps.format, 0); + + m_pDriver->GetReplay()->GetBufferData( + m_pDriver->GetResourceManager()->GetLiveID(bufData.resource), bufData.byteOffset, + bufData.byteSize, data.bytes); + } + } + } + + return data; + } + VkPipeline MakePipe(const ShaderConstParameters ¶ms, uint32_t floatBitSize, bool depthTex, bool uintTex, bool sintTex) { @@ -5080,6 +5125,87 @@ 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 d6202ad8937..ca8484e306c 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 2ec6c3b1bd0..68f763f9db7 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 4f7b3a3a973..6db8dc9234c 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 fbd15b4681e..22243e95267 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 1e7b7f7fab1..c87c1f68fb4 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;