Add context menu to BufferViewer to debug vertices, or export data

This commit is contained in:
baldurk
2017-02-16 16:57:18 +00:00
parent 8177776422
commit 5a88b280ca
3 changed files with 364 additions and 29 deletions
+322 -3
View File
@@ -25,9 +25,12 @@
#include "BufferViewer.h"
#include <QDoubleSpinBox>
#include <QFontDatabase>
#include <QMenu>
#include <QMouseEvent>
#include <QScrollBar>
#include <QTimer>
#include "Code/Resources.h"
#include "Windows/ShaderViewer.h"
#include "ui_BufferViewer.h"
class CameraWrapper
@@ -364,7 +367,7 @@ public:
}
else
{
const FormatElement &el = columnForIndex(section);
const FormatElement &el = elementForColumn(section);
if(el.format.compCount == 1)
return el.name;
@@ -422,7 +425,7 @@ public:
if(col == 1 && meshView)
return idx;
const FormatElement &el = columnForIndex(col);
const FormatElement &el = elementForColumn(col);
uint32_t instIdx = 0;
if(el.instancerate > 0)
@@ -528,7 +531,7 @@ private:
int m_ColumnCount = 0;
int reservedColumnCount() const { return (meshView ? 2 : 0); }
const FormatElement &columnForIndex(int col) const
const FormatElement &elementForColumn(int col) const
{
return columns[columnLookup[col - reservedColumnCount()]];
}
@@ -615,6 +618,80 @@ BufferViewer::BufferViewer(CaptureContext &ctx, bool meshview, QWidget *parent)
else
SetupRawView();
QMenu *exportMenu = new QMenu(this);
QAction *csv = new QAction(tr("Export to &CSV"), this);
csv->setIcon(Icons::save());
exportMenu->addAction(csv);
QAction *bytes = new QAction(tr("Export to &Bytes"), this);
bytes->setIcon(Icons::save());
exportMenu->addAction(bytes);
QAction *debug = new QAction(tr("&Debug this Vertex"), this);
debug->setIcon(Icons::wrench());
ui->exportDrop->setMenu(exportMenu);
QObject::connect(csv, &QAction::triggered, [this] { exportData(BufferExport(BufferExport::CSV)); });
QObject::connect(bytes, &QAction::triggered,
[this] { exportData(BufferExport(BufferExport::RawBytes)); });
QObject::connect(debug, &QAction::triggered, this, &BufferViewer::debugVertex);
QObject::connect(ui->exportDrop, &QToolButton::clicked,
[this] { exportData(BufferExport(BufferExport::CSV)); });
ui->vsinData->setContextMenuPolicy(Qt::CustomContextMenu);
ui->vsoutData->setContextMenuPolicy(Qt::CustomContextMenu);
ui->gsoutData->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->vsinData, &RDTableView::customContextMenuRequested,
[this, debug, csv, bytes](const QPoint &pos) {
m_CurView = ui->vsinData;
QMenu *menu = new QMenu(this);
if(m_MeshView)
{
menu->addAction(debug);
menu->addSeparator();
}
menu->addAction(csv);
menu->addAction(bytes);
menu->popup(ui->vsinData->viewport()->mapToGlobal(pos));
});
QObject::connect(ui->vsoutData, &RDTableView::customContextMenuRequested,
[this, debug, csv, bytes](const QPoint &pos) {
m_CurView = ui->vsoutData;
QMenu *menu = new QMenu(this);
if(m_MeshView)
{
menu->addAction(debug);
menu->addSeparator();
}
menu->addAction(csv);
menu->addAction(bytes);
menu->popup(ui->vsoutData->viewport()->mapToGlobal(pos));
});
QObject::connect(ui->gsoutData, &RDTableView::customContextMenuRequested,
[this, csv, bytes](const QPoint &pos) {
m_CurView = ui->gsoutData;
QMenu *menu = new QMenu(this);
menu->addAction(csv);
menu->addAction(bytes);
menu->popup(ui->gsoutData->viewport()->mapToGlobal(pos));
});
ui->dockarea->setAllowFloatingWindow(false);
ui->dockarea->setRubberBandLineWidth(50);
@@ -644,6 +721,10 @@ BufferViewer::BufferViewer(CaptureContext &ctx, bool meshview, QWidget *parent)
QObject::connect(ui->gsoutData->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&BufferViewer::data_selected);
QObject::connect(ui->vsinData, &RDTableView::clicked, [this]() { m_CurView = ui->vsinData; });
QObject::connect(ui->vsoutData, &RDTableView::clicked, [this]() { m_CurView = ui->vsoutData; });
QObject::connect(ui->gsoutData, &RDTableView::clicked, [this]() { m_CurView = ui->gsoutData; });
QObject::connect(ui->vsinData->verticalScrollBar(), &QScrollBar::valueChanged, this,
&BufferViewer::data_scrolled);
QObject::connect(ui->vsoutData->verticalScrollBar(), &QScrollBar::valueChanged, this,
@@ -1565,6 +1646,8 @@ void BufferViewer::CalcColumnWidth()
void BufferViewer::data_selected(const QItemSelection &selected, const QItemSelection &deselected)
{
m_CurView = qobject_cast<RDTableView *>(QObject::sender());
if(selected.count() > 0)
{
UpdateHighlightVerts();
@@ -1640,6 +1723,242 @@ void BufferViewer::processFormat(const QString &format)
OnEventChanged(m_Ctx.CurEvent());
}
void BufferViewer::exportData(const BufferExport &params)
{
if(!m_Ctx.LogLoaded())
return;
if(!m_Ctx.CurDrawcall())
return;
if(!m_CurView)
return;
QString filter;
if(params.format == BufferExport::CSV)
filter = "CSV Files (*.csv)";
else if(params.format == BufferExport::RawBytes)
filter = "Binary Files (*.bin)";
QString filename = RDDialog::getSaveFileName(this, tr("Export buffer to bytes"), QString(),
QString("%1;;All files (*.*)").arg(filter));
if(filename.isEmpty())
return;
QFile *f = new QFile(filename);
if(!f->open(QIODevice::WriteOnly | QFile::Truncate))
{
delete f;
RDDialog::critical(this, tr("Error exporting file"),
tr("Couldn't open file '%1' for writing").arg(filename));
return;
}
BufferItemModel *model = (BufferItemModel *)m_CurView->model();
LambdaThread *exportThread = new LambdaThread([this, params, model, f]() {
if(params.format == BufferExport::RawBytes)
{
if(!m_MeshView)
{
// this is the simplest possible case, we just dump the contents of the first buffer, as
// it's tightly packed
f->write((const char *)model->buffers[0].data,
int(model->buffers[0].end - model->buffers[0].data));
}
else
{
// cache column data for the inner loop
struct CachedElData
{
const FormatElement *el = NULL;
const char *data = NULL;
const char *end = NULL;
size_t stride;
int byteSize;
uint32_t instIdx = 0;
QByteArray nulls;
};
QVector<CachedElData> cache;
cache.reserve(model->columns.count());
for(int col = 0; col < model->columns.count(); col++)
{
const FormatElement &el = model->columns[col];
CachedElData d;
d.el = &el;
d.byteSize = el.byteSize();
d.nulls = QByteArray(d.byteSize, '\0');
if(el.instancerate > 0)
d.instIdx = model->curInstance / el.instancerate;
if(el.buffer < model->buffers.size())
{
d.data = (const char *)model->buffers[el.buffer].data;
d.end = (const char *)model->buffers[el.buffer].end;
d.stride = model->buffers[el.buffer].stride;
d.data += el.offset;
if(el.perinstance)
d.data += d.stride * d.instIdx;
}
cache.push_back(d);
}
// go row by row, finding the start of the row and dumping out the elements using their
// offset and sizes
for(int i = 0; i < model->rowCount(); i++)
{
uint32_t idx = model->data(model->index(i, 1), Qt::DisplayRole).toUInt();
for(int col = 0; col < cache.count(); col++)
{
const CachedElData &d = cache[col];
const FormatElement *el = d.el;
if(d.data)
{
const char *bytes = d.data;
if(!el->perinstance)
bytes += d.stride * idx;
if(bytes + d.byteSize <= d.end)
f->write(bytes, d.byteSize);
}
// if we didn't continue above, something was wrong, so write nulls
f->write(d.nulls);
}
}
}
}
else if(params.format == BufferExport::CSV)
{
// this works identically no matter whether we're mesh view or what, we just iterate the
// elements and call the model's data()
QTextStream s(f);
for(int i = 0; i < model->columnCount(); i++)
{
s << model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
if(i + 1 < model->columnCount())
s << ", ";
}
s << "\n";
for(int row = 0; row < model->rowCount(); row++)
{
for(int col = 0; col < model->columnCount(); col++)
{
s << model->data(model->index(row, col), Qt::DisplayRole).toString();
if(col + 1 < model->columnCount())
s << ", ";
}
s << "\n";
}
}
f->close();
delete f;
});
exportThread->start();
ShowProgressDialog(this, tr("Exporting data"),
[exportThread]() { return !exportThread->isRunning(); });
exportThread->deleteLater();
}
void BufferViewer::debugVertex()
{
if(!m_Ctx.LogLoaded())
return;
if(!m_Ctx.CurDrawcall())
return;
if(!m_CurView)
return;
QModelIndex idx = m_CurView->selectionModel()->currentIndex();
if(!idx.isValid())
{
GUIInvoke::call([this]() {
RDDialog::critical(this, tr("Error debugging"),
tr("Error debugging vertex - make sure a valid vertex is selected"));
});
return;
}
uint32_t vertid =
m_CurView->model()->data(m_CurView->model()->index(idx.row(), 0), Qt::DisplayRole).toUInt();
uint32_t index =
m_CurView->model()->data(m_CurView->model()->index(idx.row(), 1), Qt::DisplayRole).toUInt();
m_Ctx.Renderer().AsyncInvoke([this, vertid, index](IReplayRenderer *r) {
ShaderDebugTrace *trace = new ShaderDebugTrace;
bool success =
r->DebugVertex(vertid, m_Config.curInstance, index, m_Ctx.CurDrawcall()->instanceOffset,
m_Ctx.CurDrawcall()->vertexOffset, trace);
if(!success || trace->states.count == 0)
{
delete trace;
// if we couldn't debug the pixel on this event, open up a pixel history
GUIInvoke::call([this]() {
RDDialog::critical(this, tr("Error debugging"),
tr("Error debugging vertex - make sure a valid vertex is selected"));
});
return;
}
GUIInvoke::call([this, vertid, trace]() {
QString debugContext = tr("Vertex %1").arg(vertid);
if(m_Ctx.CurDrawcall()->numInstances > 1)
debugContext += tr(", Instance %1").arg(m_Config.curInstance);
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState.GetShaderReflection(eShaderStage_Pixel);
const ShaderBindpointMapping &bindMapping =
m_Ctx.CurPipelineState.GetBindpointMapping(eShaderStage_Pixel);
// viewer takes ownership of the trace
ShaderViewer *s = ShaderViewer::debugShader(m_Ctx, &bindMapping, shaderDetails,
eShaderStage_Pixel, trace, debugContext, this);
m_Ctx.setupDockWindow(s);
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
manager->addToolWindow(s, ref);
});
});
}
void BufferViewer::SyncViews(RDTableView *primary, bool selection, bool scroll)
{
if(!ui->syncViews->isChecked())
+18
View File
@@ -38,6 +38,19 @@ class CameraWrapper;
class ArcballWrapper;
class FlycamWrapper;
struct BufferExport
{
enum ExportFormat
{
CSV,
RawBytes,
};
ExportFormat format;
BufferExport(ExportFormat f) : format(f) {}
};
class BufferViewer : public QFrame, public ILogViewerForm
{
Q_OBJECT
@@ -95,6 +108,9 @@ private slots:
void processFormat(const QString &format);
void exportData(const BufferExport &params);
void debugVertex();
private:
Ui::BufferViewer *ui;
CaptureContext &m_Ctx;
@@ -131,6 +147,8 @@ private:
BufferItemModel *m_ModelVSOut;
BufferItemModel *m_ModelGSOut;
RDTableView *m_CurView = NULL;
int m_IdxColWidth;
int m_DataColWidth;
+24 -26
View File
@@ -3297,47 +3297,45 @@ void TextureViewer::on_saveTex_clicked()
void TextureViewer::on_debugPixelContext_clicked()
{
ShaderDebugTrace *trace = new ShaderDebugTrace;
if(m_PickedPoint.x() < 0 || m_PickedPoint.y() < 0)
return;
int x = m_PickedPoint.x() >> (int)m_TexDisplay.mip;
int y = m_PickedPoint.y() >> (int)m_TexDisplay.mip;
bool success = false;
m_Ctx.Renderer().AsyncInvoke([this, x, y](IReplayRenderer *r) {
ShaderDebugTrace *trace = new ShaderDebugTrace;
m_Ctx.Renderer().BlockInvoke([this, x, y, &success, trace](IReplayRenderer *r) {
success = r->DebugPixel((uint32_t)x, (uint32_t)y, m_TexDisplay.sampleIdx, ~0U, trace);
});
bool success = r->DebugPixel((uint32_t)x, (uint32_t)y, m_TexDisplay.sampleIdx, ~0U, trace);
if(!success || trace->states.count == 0)
{
delete trace;
if(!success || trace->states.count == 0)
{
delete trace;
// if we couldn't debug the pixel on this event, open up a pixel history
on_pixelHistory_clicked();
return;
}
// if we couldn't debug the pixel on this event, open up a pixel history
GUIInvoke::call([this]() { on_pixelHistory_clicked(); });
return;
}
GUIInvoke::call([this, x, y, trace]() {
QString debugContext = tr("Pixel %1,%2").arg(x).arg(y);
GUIInvoke::call([this, x, y, trace]() {
QString debugContext = tr("Pixel %1,%2").arg(x).arg(y);
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState.GetShaderReflection(eShaderStage_Pixel);
const ShaderBindpointMapping &bindMapping =
m_Ctx.CurPipelineState.GetBindpointMapping(eShaderStage_Pixel);
const ShaderReflection *shaderDetails =
m_Ctx.CurPipelineState.GetShaderReflection(eShaderStage_Pixel);
const ShaderBindpointMapping &bindMapping =
m_Ctx.CurPipelineState.GetBindpointMapping(eShaderStage_Pixel);
// viewer takes ownership of the trace
ShaderViewer *s = ShaderViewer::debugShader(m_Ctx, &bindMapping, shaderDetails,
eShaderStage_Pixel, trace, debugContext, this);
// viewer takes ownership of the trace
ShaderViewer *s = ShaderViewer::debugShader(m_Ctx, &bindMapping, shaderDetails,
eShaderStage_Pixel, trace, debugContext, this);
m_Ctx.setupDockWindow(s);
m_Ctx.setupDockWindow(s);
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
ToolWindowManager *manager = ToolWindowManager::managerOf(this);
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
manager->addToolWindow(s, ref);
ToolWindowManager::AreaReference ref(ToolWindowManager::AddTo, manager->areaOf(this));
manager->addToolWindow(s, ref);
});
});
}