From 7bdf26d3293d192d4706a0ebb8797d6d1e33aa3f Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 11 Jul 2022 13:02:23 +0100 Subject: [PATCH] Implement location tracking and goto for GS printfs * We can't debug geometry shaders but we can scroll to them, as long as we have the primitive. We can't differentiate instances currently without passing that data through from the VS (and through tessellation, if it exists). * This also disables the debug and goto buttons for printfs from shader stages that don't support those operations. --- qrenderdoc/Code/Interface/QRDInterface.h | 6 ++ qrenderdoc/Windows/BufferViewer.cpp | 10 +++ qrenderdoc/Windows/BufferViewer.h | 1 + qrenderdoc/Windows/ShaderMessageViewer.cpp | 73 +++++++++++++++++-- qrenderdoc/Windows/ShaderMessageViewer.h | 3 +- renderdoc/api/replay/common_pipestate.h | 29 ++++++++ .../driver/vulkan/vk_shader_feedback.cpp | 30 +++++++- renderdoc/replay/renderdoc_serialise.inl | 8 ++ 8 files changed, 150 insertions(+), 10 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 8183274f1..2a728b72f 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -778,6 +778,12 @@ QWidget. )"); virtual void ScrollToColumn(int32_t column, MeshDataStage stage = MeshDataStage::VSIn) = 0; + DOCUMENT(R"(Ensure the given stage's data is visible and raised, if it wasn't before. + +:param renderdoc.MeshDataStage stage: The stage of the geometry pipeline to show data for. +)"); + virtual void ShowMeshData(MeshDataStage stage) = 0; + DOCUMENT(R"(For a mesh view, set the current instance. This is ignored when called on a raw buffer view. diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 1f2ee5b20..f17ee8f79 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -4150,6 +4150,16 @@ void BufferViewer::ScrollToColumn(RDTableView *view, int column) view->verticalScrollBar()->setValue(vs); } +void BufferViewer::ShowMeshData(MeshDataStage stage) +{ + if(stage == MeshDataStage::VSIn) + ToolWindowManager::raiseToolWindow(ui->vsinData); + else if(stage == MeshDataStage::VSOut) + ToolWindowManager::raiseToolWindow(ui->vsoutData); + else if(stage == MeshDataStage::GSOut) + ToolWindowManager::raiseToolWindow(ui->gsoutData); +} + void BufferViewer::SetCurrentInstance(int32_t instance) { if(ui->instance->isVisible() && ui->instance->isEnabled()) diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index 891d5d9c1..33ece2c13 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -104,6 +104,7 @@ public: QWidget *Widget() override { return this; } void ScrollToRow(int32_t row, MeshDataStage stage = MeshDataStage::VSIn) override; void ScrollToColumn(int32_t column, MeshDataStage stage = MeshDataStage::VSIn) override; + void ShowMeshData(MeshDataStage stage) override; void SetCurrentInstance(int32_t instance) override; void SetCurrentView(int32_t view) override; void SetPreviewStage(MeshDataStage stage) override; diff --git a/qrenderdoc/Windows/ShaderMessageViewer.cpp b/qrenderdoc/Windows/ShaderMessageViewer.cpp index b2aaaeb1a..137641a7b 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.cpp +++ b/qrenderdoc/Windows/ShaderMessageViewer.cpp @@ -34,8 +34,11 @@ #include "toolwindowmanager/ToolWindowManager.h" #include "ui_ShaderMessageViewer.h" -ButtonDelegate::ButtonDelegate(const QIcon &icon, QWidget *parent) - : m_Icon(icon), QStyledItemDelegate(parent) +static const int debuggableRole = Qt::UserRole + 1; +static const int gotoableRole = Qt::UserRole + 2; + +ButtonDelegate::ButtonDelegate(const QIcon &icon, int enableRole, QWidget *parent) + : m_Icon(icon), m_EnableRole(enableRole), QStyledItemDelegate(parent) { } @@ -54,7 +57,9 @@ void ButtonDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option button.rect.setSize(sz); button.icon = m_Icon; button.iconSize = sz; - button.state = QStyle::State_Enabled; + + if(m_EnableRole == 0 || index.data(m_EnableRole).toBool()) + button.state = QStyle::State_Enabled; if(m_ClickedIndex == index) button.state |= QStyle::State_Sunken; @@ -77,7 +82,8 @@ bool ButtonDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, { if(event->type() == QEvent::MouseButtonPress) { - m_ClickedIndex = index; + if(m_EnableRole == 0 || index.data(m_EnableRole).toBool()) + m_ClickedIndex = index; } else if(event->type() == QEvent::MouseMove) { @@ -105,7 +111,7 @@ bool ButtonDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, } else if(event->type() == QEvent::MouseButtonRelease) { - if(m_ClickedIndex == index) + if(m_ClickedIndex == index && index != QModelIndex()) { m_ClickedIndex = QModelIndex(); @@ -200,7 +206,7 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s int sortColumn = 0; - m_debugDelegate = new ButtonDelegate(Icons::wrench(), this); + m_debugDelegate = new ButtonDelegate(Icons::wrench(), debuggableRole, this); if(m_Action && (m_Action->flags & ActionFlags::Dispatch)) { @@ -218,7 +224,7 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s ui->messages->setColumns({lit("Debug"), lit("Go to"), tr("Location"), lit("Message")}); sortColumn = 2; - m_gotoDelegate = new ButtonDelegate(Icons::find(), this); + m_gotoDelegate = new ButtonDelegate(Icons::find(), gotoableRole, this); ui->messages->setItemDelegateForColumn(0, m_debugDelegate); ui->messages->setItemDelegateForColumn(1, m_gotoDelegate); @@ -389,6 +395,9 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s m_Ctx.ShowMeshPreview(); m_Ctx.GetMeshPreview()->SetCurrentInstance(msg.location.vertex.instance); m_Ctx.GetMeshPreview()->SetCurrentView(msg.location.vertex.view); + m_Ctx.GetMeshPreview()->ShowMeshData(MeshDataStage::VSOut); + m_Ctx.GetMeshPreview()->ScrollToRow(msg.location.vertex.vertexIndex, MeshDataStage::VSOut); + m_Ctx.GetMeshPreview()->ShowMeshData(MeshDataStage::VSIn); // TODO, not accurate for indices m_Ctx.GetMeshPreview()->ScrollToRow(msg.location.vertex.vertexIndex, MeshDataStage::VSIn); } @@ -415,6 +424,17 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s ShaderStage::Pixel, 0, 0); m_Ctx.GetTextureViewer()->GotoLocation(msg.location.pixel.x, msg.location.pixel.y); } + else if(msg.stage == ShaderStage::Geometry) + { + m_Ctx.ShowMeshPreview(); + m_Ctx.GetMeshPreview()->SetCurrentView(msg.location.geometry.view); + m_Ctx.GetMeshPreview()->ShowMeshData(MeshDataStage::GSOut); + // TODO, instances not supported + m_Ctx.GetMeshPreview()->ScrollToRow( + RENDERDOC_VertexOffset(m_Ctx.CurPipelineState().GetPrimitiveTopology(), + msg.location.geometry.primitive), + MeshDataStage::GSOut); + } else { qCritical() << "Can't go to a compute thread"; @@ -496,6 +516,15 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s // column 2 is the thread column for compute return am.location.compute.thread < bm.location.compute.thread; } + else if(am.stage == ShaderStage::Geometry) + { + const ShaderGeometryMessageLocation &aloc = am.location.geometry; + const ShaderGeometryMessageLocation &bloc = bm.location.geometry; + + if(aloc.view != bloc.view) + return aloc.view < bloc.view; + return am.location.geometry.primitive < bm.location.geometry.primitive; + } else { // can't sort these, pretend they're all equal @@ -735,6 +764,10 @@ void ShaderMessageViewer::refreshMessages() QString filter = ui->filter->text().trimmed(); + const ShaderReflection *vsrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Vertex); + const ShaderReflection *psrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel); + const ShaderReflection *csrefl = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); + for(int i = 0; i < m_Messages.count(); i++) { const ShaderMessage &msg = m_Messages[i]; @@ -745,9 +778,13 @@ void ShaderMessageViewer::refreshMessages() QString text(msg.message); + const ShaderReflection *refl = NULL; + QString location; if(msg.stage == ShaderStage::Vertex) { + refl = vsrefl; + // only show the view if the draw has multiview enabled if(m_Multiview) { @@ -771,6 +808,8 @@ void ShaderMessageViewer::refreshMessages() } else if(msg.stage == ShaderStage::Pixel) { + refl = psrefl; + location = QFormatStr("%1 %2,%3") .arg(IsD3D(m_API) ? lit("Pixel") : lit("Frag")) .arg(msg.location.pixel.x) @@ -794,11 +833,22 @@ void ShaderMessageViewer::refreshMessages() } else if(msg.stage == ShaderStage::Compute) { + refl = csrefl; + } + else if(msg.stage == ShaderStage::Geometry) + { + location = lit("Geometry Prim %1").arg(msg.location.geometry.primitive); + + // only show the view if the draw has multiview enabled + if(m_Multiview) + { + location += lit(", View %1").arg(msg.location.geometry.view); + } } else { // no location info for other stages - location = tr("Unknown shader"); + location = tr("Unknown %1").arg(ToQStr(msg.stage, m_Ctx.APIProps().pipelineType)); } // filter by text on location and messag @@ -821,10 +871,17 @@ void ShaderMessageViewer::refreshMessages() .arg(msg.location.compute.thread[2]), text, }); + + node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); } else { node = new RDTreeWidgetItem({QString(), QString(), location, text}); + + node->setData(0, debuggableRole, refl && refl->debugInfo.debuggable); + node->setData(1, gotoableRole, msg.stage == ShaderStage::Vertex || + msg.stage == ShaderStage::Pixel || + msg.stage == ShaderStage::Geometry); } if(node) diff --git a/qrenderdoc/Windows/ShaderMessageViewer.h b/qrenderdoc/Windows/ShaderMessageViewer.h index db5f5ad3c..493498311 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.h +++ b/qrenderdoc/Windows/ShaderMessageViewer.h @@ -40,9 +40,10 @@ private: QModelIndex m_ClickedIndex; QIcon m_Icon; + int m_EnableRole; public: - ButtonDelegate(const QIcon &icon, QWidget *parent); + ButtonDelegate(const QIcon &icon, int enableRole, QWidget *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 9f67e9e46..d7f7fd02b 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -632,6 +632,29 @@ struct ShaderPixelMessageLocation DECLARE_REFLECTION_STRUCT(ShaderPixelMessageLocation); +DOCUMENT(R"(A geometry shader message's location. + +.. data:: NoLocation + + No frame number is available. +)"); +struct ShaderGeometryMessageLocation +{ + DOCUMENT(R"(The primitive index + +:type: int +)"); + uint32_t primitive; + + DOCUMENT(R"(The multiview view for this primitive, or ``0`` if multiview is disabled. + +:type: int +)"); + uint32_t view; +}; + +DECLARE_REFLECTION_STRUCT(ShaderGeometryMessageLocation); + DOCUMENT("A shader message's location."); union ShaderMessageLocation { @@ -652,6 +675,12 @@ union ShaderMessageLocation :type: ShaderPixelMessageLocation )"); ShaderPixelMessageLocation pixel; + + DOCUMENT(R"(The location if the shader is a geometry shader. + +:type: ShaderGeometryMessageLocation +)"); + ShaderGeometryMessageLocation geometry; }; DECLARE_REFLECTION_STRUCT(ShaderMessageLocation); diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp index 4ab4aae8f..290601af1 100644 --- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp @@ -357,7 +357,8 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat rdcspv::Id bufferAddressConst, ssboVar, uint32ptrtype; - if(usesMultiview && (stage == ShaderStage::Pixel || stage == ShaderStage::Vertex)) + if(usesMultiview && + (stage == ShaderStage::Pixel || stage == ShaderStage::Vertex || stage == ShaderStage::Geometry)) { editor.AddCapability(rdcspv::Capability::MultiView); editor.AddExtension("SPV_KHR_multiview"); @@ -683,6 +684,28 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {coord, view, prim})); } } + else if(stage == ShaderStage::Geometry) + { + rdcspv::Id prim = fetchOrAddGlobalInput("rdoc_primitiveIndex", ShaderBuiltin::PrimitiveIndex, + rdcspv::BuiltIn::PrimitiveId, uint32Type); + + rdcspv::Id view; + + // only search for the view index is the multiview capability is declared, otherwise it's + // invalid and we just set 0. Valid for both Vertex and Pixel shaders + if(editor.HasCapability(rdcspv::Capability::MultiView)) + { + view = fetchOrAddGlobalInput("rdoc_viewIndex", ShaderBuiltin::ViewportIndex, + rdcspv::BuiltIn::ViewIndex, uint32Type); + } + else + { + view = editor.AddConstantImmediate(0U); + } + + location = locationGather.add( + rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {prim, view, zero})); + } else { RDCWARN("No identifier stored for %s stage", ToStr(stage).c_str()); @@ -1900,6 +1923,11 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) msg.location.vertex.instance = location[1] - action->instanceOffset; msg.location.vertex.view = location[2]; } + else if(stage == ShaderStage::Geometry) + { + msg.location.geometry.primitive = location[0]; + msg.location.geometry.view = location[1]; + } else { msg.location.pixel.x = location[0] >> 16U; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 113ca8e08..12a9c7188 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1045,6 +1045,14 @@ void DoSerialise(SerialiserType &ser, ShaderPixelMessageLocation &el) SIZE_CHECK(20); } +template +void DoSerialise(SerialiserType &ser, ShaderGeometryMessageLocation &el) +{ + SERIALISE_MEMBER(primitive); + + SIZE_CHECK(4); +} + template void DoSerialise(SerialiserType &ser, ShaderMessageLocation &el) {