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) {