mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
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.
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<uint32_t>(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;
|
||||
|
||||
@@ -1045,6 +1045,14 @@ void DoSerialise(SerialiserType &ser, ShaderPixelMessageLocation &el)
|
||||
SIZE_CHECK(20);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, ShaderGeometryMessageLocation &el)
|
||||
{
|
||||
SERIALISE_MEMBER(primitive);
|
||||
|
||||
SIZE_CHECK(4);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, ShaderMessageLocation &el)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user