mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-25 16:06:31 +00:00
Add options to filter and correlate between task and mesh invocations
This commit is contained in:
@@ -515,6 +515,7 @@ struct BufferConfiguration
|
||||
|
||||
rdcarray<TaskGroupSize> taskSizes;
|
||||
rdcarray<uint32_t> meshletVertexPrefixCounts;
|
||||
uint32_t taskOrMeshletOffset = 0;
|
||||
uint64_t perPrimitiveOffset = 0;
|
||||
uint32_t perPrimitiveStride = 0;
|
||||
Topology topology = Topology::TriangleList;
|
||||
@@ -562,6 +563,7 @@ struct BufferConfiguration
|
||||
baseVertex = o.baseVertex;
|
||||
meshletVertexPrefixCounts = o.meshletVertexPrefixCounts;
|
||||
taskSizes = o.taskSizes;
|
||||
taskOrMeshletOffset = o.taskOrMeshletOffset;
|
||||
perPrimitiveOffset = o.perPrimitiveOffset;
|
||||
perPrimitiveStride = o.perPrimitiveStride;
|
||||
topology = o.topology;
|
||||
@@ -1185,7 +1187,9 @@ public:
|
||||
|
||||
size_t meshletIdx = it - config.meshletVertexPrefixCounts.begin();
|
||||
|
||||
return QFormatStr("%1[%2]").arg(meshletIdx).arg(row + config.pagingOffset - *it);
|
||||
return QFormatStr("%1[%2]")
|
||||
.arg(meshletIdx + config.taskOrMeshletOffset)
|
||||
.arg(row + config.pagingOffset - *it);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1950,9 +1954,6 @@ static void RT_FetchMeshPipeData(IReplayController *r, ICaptureContext &ctx, Pop
|
||||
|
||||
data->out1Config.statusString = data->postOut1.status;
|
||||
|
||||
data->out2Config.numRows = data->postOut2.numIndices;
|
||||
data->out2Config.unclampedNumRows = 0;
|
||||
|
||||
if(data->out2Config.indices)
|
||||
data->out2Config.indices->deref();
|
||||
if(data->out2Config.displayIndices)
|
||||
@@ -1966,6 +1967,9 @@ static void RT_FetchMeshPipeData(IReplayController *r, ICaptureContext &ctx, Pop
|
||||
count += meshletSize.numIndices;
|
||||
}
|
||||
|
||||
data->out2Config.numRows = numIndices;
|
||||
data->out2Config.unclampedNumRows = 0;
|
||||
|
||||
data->out2Config.topology = data->postOut2.topology;
|
||||
data->out2Config.perPrimitiveOffset = data->postOut2.perPrimitiveOffset;
|
||||
data->out2Config.perPrimitiveStride = data->postOut2.perPrimitiveStride;
|
||||
@@ -2431,6 +2435,15 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent)
|
||||
m_DebugVert = new QAction(tr("&Debug this Vertex"), this);
|
||||
m_DebugVert->setIcon(Icons::wrench());
|
||||
|
||||
m_FilterMesh = new QAction(tr("&Filter to this Meshlet"), this);
|
||||
m_FilterMesh->setIcon(Icons::filter());
|
||||
|
||||
m_RemoveFilter = new QAction(tr("&Remove Filter"), this);
|
||||
m_RemoveFilter->setIcon(Icons::arrow_undo());
|
||||
|
||||
m_GotoTask = new QAction(tr("&Go to task"), this);
|
||||
m_GotoTask->setIcon(Icons::arrow_join());
|
||||
|
||||
ui->exportDrop->setMenu(m_ExportMenu);
|
||||
|
||||
QObject::connect(m_ExportMenu, &QMenu::aboutToShow, this, &BufferViewer::updateExportActionNames);
|
||||
@@ -2440,6 +2453,39 @@ 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_RemoveFilter, &QAction::triggered,
|
||||
[this]() { SetMeshFilter(MeshFilter::None); });
|
||||
QObject::connect(m_FilterMesh, &QAction::triggered, [this]() {
|
||||
QModelIndex idx = m_CurView->selectionModel()->currentIndex();
|
||||
|
||||
if(!idx.isValid())
|
||||
return;
|
||||
|
||||
uint32_t taskIndex = 0, meshletIndex = 0;
|
||||
GetIndicesForMeshRow((uint32_t)idx.row(), taskIndex, meshletIndex);
|
||||
|
||||
SetMeshFilter(MeshFilter::Mesh, taskIndex, meshletIndex);
|
||||
});
|
||||
QObject::connect(m_GotoTask, &QAction::triggered, [this]() {
|
||||
// if there's a filter then by definition only one task is visible, just scroll to it
|
||||
if(m_CurMeshFilter != MeshFilter::None)
|
||||
{
|
||||
ShowMeshData(MeshDataStage::TaskOut);
|
||||
ScrollToRow(0, MeshDataStage::TaskOut);
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex idx = m_CurView->selectionModel()->currentIndex();
|
||||
|
||||
if(!idx.isValid())
|
||||
return;
|
||||
|
||||
uint32_t taskIndex = 0, meshletIndex = 0;
|
||||
GetIndicesForMeshRow((uint32_t)idx.row(), taskIndex, meshletIndex);
|
||||
|
||||
ShowMeshData(MeshDataStage::TaskOut);
|
||||
ScrollToRow((int)taskIndex, MeshDataStage::TaskOut);
|
||||
});
|
||||
|
||||
QObject::connect(ui->exportDrop, &QToolButton::clicked,
|
||||
[this] { exportData(BufferExport(BufferExport::CSV)); });
|
||||
@@ -2577,6 +2623,36 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent)
|
||||
m_Ctx.AddCaptureViewer(this);
|
||||
}
|
||||
|
||||
void BufferViewer::GetIndicesForMeshRow(uint32_t row, uint32_t &taskIndex, uint32_t &meshletIdx)
|
||||
{
|
||||
const BufferConfiguration &config2 = m_ModelOut2->getConfig();
|
||||
|
||||
auto it = std::upper_bound(config2.meshletVertexPrefixCounts.begin(),
|
||||
config2.meshletVertexPrefixCounts.end(), row);
|
||||
|
||||
if(it != config2.meshletVertexPrefixCounts.begin())
|
||||
it--;
|
||||
|
||||
meshletIdx = uint32_t(it - config2.meshletVertexPrefixCounts.begin());
|
||||
|
||||
const BufferConfiguration &config1 = m_ModelOut1->getConfig();
|
||||
|
||||
taskIndex = 0;
|
||||
|
||||
uint32_t meshletCounter = 0;
|
||||
for(taskIndex = 0; taskIndex < meshletIdx && taskIndex < config1.taskSizes.size(); taskIndex++)
|
||||
{
|
||||
meshletCounter += config1.taskSizes[taskIndex].x * config1.taskSizes[taskIndex].y *
|
||||
config1.taskSizes[taskIndex].z;
|
||||
|
||||
if(meshletIdx < meshletCounter)
|
||||
break;
|
||||
}
|
||||
|
||||
taskIndex += config1.taskOrMeshletOffset;
|
||||
meshletIdx += config2.taskOrMeshletOffset;
|
||||
}
|
||||
|
||||
void BufferViewer::SetupRawView()
|
||||
{
|
||||
ui->formatSpecifier->setVisible(true);
|
||||
@@ -2592,6 +2668,9 @@ void BufferViewer::SetupRawView()
|
||||
ui->viewIndex->setVisible(false);
|
||||
ui->dockarea->setVisible(false);
|
||||
|
||||
ui->meshFilterLabel->setVisible(false);
|
||||
ui->resetMeshFilterButton->setVisible(false);
|
||||
|
||||
ui->inTable->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
ui->inTable->setPinnedColumns(1);
|
||||
@@ -2692,6 +2771,9 @@ void BufferViewer::SetupMeshView()
|
||||
ui->byteRangeLengthLabel->setVisible(false);
|
||||
byteRangeLength->setVisible(false);
|
||||
|
||||
ui->meshFilterLabel->setVisible(false);
|
||||
ui->resetMeshFilterButton->setVisible(false);
|
||||
|
||||
ui->fixedVars->setVisible(false);
|
||||
ui->showPadding->setVisible(false);
|
||||
|
||||
@@ -2870,15 +2952,23 @@ void BufferViewer::fixedVars_contextMenu(const QPoint &pos)
|
||||
QAction collapseAll(tr("C&ollapse All"), this);
|
||||
QAction copy(tr("&Copy"), this);
|
||||
QAction showPadding(tr("&Show Padding"), this);
|
||||
QAction removeFilter(tr("&Remove Filter"), this);
|
||||
QAction filterTask(tr("&Filter to this Task"), this);
|
||||
QAction gotoMesh(tr("&Go to meshes"), this);
|
||||
|
||||
expandAll.setIcon(Icons::arrow_out());
|
||||
collapseAll.setIcon(Icons::arrow_in());
|
||||
copy.setIcon(Icons::copy());
|
||||
showPadding.setIcon(Icons::align());
|
||||
removeFilter.setIcon(Icons::arrow_undo());
|
||||
filterTask.setIcon(Icons::filter());
|
||||
gotoMesh.setIcon(Icons::arrow_join());
|
||||
showPadding.setCheckable(true);
|
||||
showPadding.setChecked(ui->showPadding->isChecked());
|
||||
|
||||
expandAll.setEnabled(item && item->childCount() > 0);
|
||||
removeFilter.setEnabled(item && m_CurMeshFilter != MeshFilter::None);
|
||||
filterTask.setEnabled(item);
|
||||
gotoMesh.setEnabled(item);
|
||||
collapseAll.setEnabled(expandAll.isEnabled());
|
||||
|
||||
contextMenu.addAction(&expandAll);
|
||||
@@ -2887,14 +2977,63 @@ void BufferViewer::fixedVars_contextMenu(const QPoint &pos)
|
||||
|
||||
contextMenu.addSeparator();
|
||||
|
||||
if(!m_MeshView)
|
||||
int idx = ui->fixedVars->indexOfTopLevelItem(item);
|
||||
|
||||
if(m_MeshView)
|
||||
{
|
||||
contextMenu.addAction(&removeFilter);
|
||||
contextMenu.addAction(&filterTask);
|
||||
contextMenu.addAction(&gotoMesh);
|
||||
|
||||
// if we're already filtering to a task, don't offer to filter any more. However if we're
|
||||
// filtered to a mesh allow 'broadening' the filter back to the task
|
||||
filterTask.setEnabled(m_CurMeshFilter != MeshFilter::TaskGroup);
|
||||
|
||||
const BufferConfiguration &config1 = m_ModelOut1->getConfig();
|
||||
if(config1.taskSizes[idx].x * config1.taskSizes[idx].y * config1.taskSizes[idx].z == 0)
|
||||
gotoMesh.setEnabled(false);
|
||||
|
||||
// if there's a filter don't enable goto mesh as normally we just scroll to the first mesh - it
|
||||
// would be redundant and potentially annoying to be able to and doesn't do anything useful
|
||||
if(m_CurMeshFilter != MeshFilter::None)
|
||||
gotoMesh.setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMenu.addAction(&showPadding);
|
||||
}
|
||||
|
||||
contextMenu.addSeparator();
|
||||
|
||||
contextMenu.addAction(m_ExportCSV);
|
||||
contextMenu.addAction(m_ExportBytes);
|
||||
|
||||
QObject::connect(&removeFilter, &QAction::triggered,
|
||||
[this, idx]() { SetMeshFilter(MeshFilter::None); });
|
||||
QObject::connect(&filterTask, &QAction::triggered, [this, idx]() {
|
||||
// if there's no filter, select this task. If we were mesh filtering, filter back to all meshes
|
||||
// under the current task (don't use idx there, since it will just be 0)
|
||||
if(m_CurMeshFilter == MeshFilter::None)
|
||||
SetMeshFilter(MeshFilter::TaskGroup, idx);
|
||||
else
|
||||
SetMeshFilter(MeshFilter::TaskGroup, m_FilteredTaskGroup);
|
||||
});
|
||||
QObject::connect(&gotoMesh, &QAction::triggered, [this, idx]() {
|
||||
const BufferConfiguration &config1 = m_ModelOut1->getConfig();
|
||||
|
||||
uint32_t meshletIndex = 0;
|
||||
for(int i = 0; i < idx && i < config1.taskSizes.count(); i++)
|
||||
{
|
||||
meshletIndex += config1.taskSizes[i].x * config1.taskSizes[i].y * config1.taskSizes[i].z;
|
||||
}
|
||||
|
||||
const BufferConfiguration &config2 = m_ModelOut2->getConfig();
|
||||
uint32_t vertexOffset = config2.meshletVertexPrefixCounts[meshletIndex];
|
||||
|
||||
ShowMeshData(MeshDataStage::MeshOut);
|
||||
ScrollToRow((int)vertexOffset, MeshDataStage::MeshOut);
|
||||
});
|
||||
|
||||
QObject::connect(&expandAll, &QAction::triggered,
|
||||
[this, item]() { ui->fixedVars->expandAllItems(item); });
|
||||
|
||||
@@ -2919,6 +3058,35 @@ void BufferViewer::stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint &
|
||||
|
||||
menu->setToolTipsVisible(true);
|
||||
|
||||
QModelIndex idx = m_CurView->selectionModel()->currentIndex();
|
||||
|
||||
const ActionDescription *action = m_Ctx.CurAction();
|
||||
|
||||
if(action && (action->flags & ActionFlags::MeshDispatch))
|
||||
{
|
||||
if(stage == MeshDataStage::GSOut)
|
||||
{
|
||||
const BufferConfiguration &config = m_ModelOut2->getConfig();
|
||||
|
||||
auto it = std::upper_bound(config.meshletVertexPrefixCounts.begin(),
|
||||
config.meshletVertexPrefixCounts.end(), (uint32_t)idx.row());
|
||||
|
||||
if(it != config.meshletVertexPrefixCounts.begin())
|
||||
it--;
|
||||
|
||||
size_t meshletIdx = it - config.meshletVertexPrefixCounts.begin();
|
||||
|
||||
m_RemoveFilter->setEnabled(m_CurMeshFilter != MeshFilter::None);
|
||||
|
||||
menu->addAction(m_RemoveFilter);
|
||||
menu->addAction(m_FilterMesh);
|
||||
menu->addAction(m_GotoTask);
|
||||
menu->addSeparator();
|
||||
|
||||
m_GotoTask->setEnabled(m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Task));
|
||||
}
|
||||
}
|
||||
|
||||
if(m_MeshView && stage != MeshDataStage::GSOut)
|
||||
{
|
||||
const ShaderReflection *shaderDetails =
|
||||
@@ -2970,8 +3138,6 @@ void BufferViewer::stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint &
|
||||
else if(stage == MeshDataStage::MeshOut)
|
||||
contextMenu = ContextMenu::MeshPreview_MeshOutVertex;
|
||||
|
||||
QModelIndex idx = m_CurView->selectionModel()->currentIndex();
|
||||
|
||||
ExtensionCallbackData callbackdata = {make_pyarg("stage", (uint32_t)stage)};
|
||||
|
||||
if(idx.isValid())
|
||||
@@ -3042,6 +3208,12 @@ void BufferViewer::FillScrolls(PopulateBufferData *bufdata)
|
||||
bufdata->inVert = ui->inTable->indexAt(QPoint(0, 0)).row();
|
||||
bufdata->out1Vert = ui->out1Table->indexAt(QPoint(0, 0)).row();
|
||||
bufdata->out2Vert = ui->out2Table->indexAt(QPoint(0, 0)).row();
|
||||
|
||||
if(bufdata->meshDispatch)
|
||||
{
|
||||
bufdata->out1Horiz = ui->fixedVars->horizontalScrollBar()->value();
|
||||
bufdata->out1Vert = ui->fixedVars->indexOfTopLevelItem(ui->fixedVars->itemAt(QPoint(0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
@@ -3260,6 +3432,86 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
bufdata->postOut1 = r->GetPostVSData(0, bufdata->inConfig.curView, MeshDataStage::TaskOut);
|
||||
bufdata->postOut2 = r->GetPostVSData(0, bufdata->inConfig.curView, MeshDataStage::MeshOut);
|
||||
|
||||
// apply mesh/task filtering to mesh data here, which will also propagate to preview
|
||||
if(m_FilteredMeshGroup != ~0U)
|
||||
{
|
||||
bufdata->out1Config.taskOrMeshletOffset = m_FilteredTaskGroup;
|
||||
|
||||
// find this meshlet's offset in the index buffer and filter to only it
|
||||
uint32_t indexCount = 0, vertexCount = 0;
|
||||
for(uint32_t i = 0; i <= m_FilteredMeshGroup && i < bufdata->postOut2.meshletSizes.size();
|
||||
i++)
|
||||
{
|
||||
MeshletSize meshletSize = bufdata->postOut2.meshletSizes[i];
|
||||
uint32_t numIndices = meshletSize.numIndices;
|
||||
if(i == m_FilteredMeshGroup)
|
||||
{
|
||||
bufdata->postOut2.meshletIndexOffset = vertexCount;
|
||||
bufdata->postOut2.meshletOffset = m_FilteredMeshGroup;
|
||||
bufdata->out2Config.taskOrMeshletOffset = m_FilteredMeshGroup;
|
||||
bufdata->postOut2.numIndices = numIndices;
|
||||
bufdata->postOut2.meshletSizes = {meshletSize};
|
||||
bufdata->postOut2.indexByteOffset += indexCount * bufdata->postOut2.indexByteStride;
|
||||
}
|
||||
indexCount += numIndices;
|
||||
vertexCount += meshletSize.numVertices;
|
||||
}
|
||||
}
|
||||
else if(m_FilteredTaskGroup != ~0U)
|
||||
{
|
||||
bufdata->out1Config.taskOrMeshletOffset = m_FilteredTaskGroup;
|
||||
|
||||
// find the relevant task and which mesh indices it corresponds to
|
||||
uint32_t meshletCounter = 0;
|
||||
for(uint32_t taskIndex = 0;
|
||||
taskIndex <= m_FilteredTaskGroup && taskIndex < bufdata->postOut1.taskSizes.size();
|
||||
taskIndex++)
|
||||
{
|
||||
uint32_t numMeshesInTask = bufdata->postOut1.taskSizes[taskIndex].x *
|
||||
bufdata->postOut1.taskSizes[taskIndex].y *
|
||||
bufdata->postOut1.taskSizes[taskIndex].z;
|
||||
|
||||
// once we've found the desired task, filter our view to only its meshes
|
||||
if(taskIndex == m_FilteredTaskGroup)
|
||||
{
|
||||
bufdata->postOut2.numIndices = 0;
|
||||
|
||||
rdcarray<MeshletSize> meshletSizes;
|
||||
meshletSizes.reserve(numMeshesInTask);
|
||||
|
||||
uint32_t indexCount = 0, vertexCount = 0;
|
||||
for(uint32_t i = 0;
|
||||
i < meshletCounter + numMeshesInTask && i < bufdata->postOut2.meshletSizes.size();
|
||||
i++)
|
||||
{
|
||||
uint32_t indicesInMeshlet = bufdata->postOut2.meshletSizes[i].numIndices;
|
||||
|
||||
if(i >= meshletCounter)
|
||||
{
|
||||
bufdata->postOut2.numIndices += indicesInMeshlet;
|
||||
meshletSizes.push_back(bufdata->postOut2.meshletSizes[i]);
|
||||
}
|
||||
|
||||
if(i == meshletCounter)
|
||||
{
|
||||
bufdata->postOut2.meshletIndexOffset = vertexCount;
|
||||
bufdata->postOut2.meshletOffset = meshletCounter;
|
||||
bufdata->out2Config.taskOrMeshletOffset = meshletCounter;
|
||||
bufdata->postOut2.indexByteOffset += indexCount * bufdata->postOut2.indexByteStride;
|
||||
}
|
||||
indexCount += indicesInMeshlet;
|
||||
vertexCount += bufdata->postOut2.meshletSizes[i].numVertices;
|
||||
}
|
||||
|
||||
bufdata->postOut2.meshletSizes = meshletSizes;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
meshletCounter += numMeshesInTask;
|
||||
}
|
||||
}
|
||||
|
||||
RT_FetchMeshPipeData(r, m_Ctx, bufdata);
|
||||
}
|
||||
else
|
||||
@@ -3499,20 +3751,25 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
{
|
||||
TaskGroupSize size = bufdata->out1Config.taskSizes[i];
|
||||
|
||||
RDTreeWidgetItem *n = new RDTreeWidgetItem(
|
||||
{QFormatStr("%1, %2, %3").arg(x).arg(y).arg(z),
|
||||
QFormatStr("Dispatched [%1, %2, %3]").arg(size.x).arg(size.y).arg(size.z),
|
||||
lit("Task Group")});
|
||||
RDTreeWidgetItem *n = NULL;
|
||||
|
||||
if(!bufdata->out1Config.columns.empty())
|
||||
if(m_CurMeshFilter == MeshFilter::None || m_FilteredTaskGroup == i)
|
||||
{
|
||||
n = new RDTreeWidgetItem(
|
||||
{QFormatStr("%1, %2, %3").arg(x).arg(y).arg(z),
|
||||
QFormatStr("Dispatched [%1, %2, %3]").arg(size.x).arg(size.y).arg(size.z),
|
||||
lit("Task Group")});
|
||||
|
||||
ui->fixedVars->addTopLevelItem(n);
|
||||
}
|
||||
|
||||
if(n && !bufdata->out1Config.columns.empty())
|
||||
{
|
||||
UI_AddTaskPayloads(n, i * bufdata->out1Config.buffers[0]->stride,
|
||||
bufdata->out1Config.columns, bufdata->out1Config.buffers[0]);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
ui->fixedVars->addTopLevelItem(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3530,6 +3787,12 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
0);
|
||||
else
|
||||
ui->fixedVars->applyExpansion(state, 0);
|
||||
|
||||
if(bufdata->out1Vert >= 0 && bufdata->out1Vert < ui->fixedVars->topLevelItemCount())
|
||||
{
|
||||
ScrollToRow(bufdata->out1Vert, MeshDataStage::TaskOut);
|
||||
ui->fixedVars->horizontalScrollBar()->setValue(bufdata->out1Horiz);
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_MeshView)
|
||||
@@ -4795,6 +5058,13 @@ void BufferViewer::ViewTexture(ResourceId id, const Subresource &sub, const rdcs
|
||||
|
||||
void BufferViewer::ScrollToRow(int32_t row, MeshDataStage stage)
|
||||
{
|
||||
if(m_MeshView && stage == MeshDataStage::TaskOut)
|
||||
{
|
||||
ui->fixedVars->scrollToItem(ui->fixedVars->topLevelItem(row));
|
||||
ui->fixedVars->setSelectedItem(ui->fixedVars->topLevelItem(row));
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollToRow(tableForStage(stage), row);
|
||||
|
||||
if(m_MeshView)
|
||||
@@ -4972,6 +5242,11 @@ void BufferViewer::updateLabelsAndLayout()
|
||||
m_Containers[1]->layout()->addWidget(ui->out2Table);
|
||||
m_Containers[2]->layout()->addWidget(ui->inTable);
|
||||
|
||||
ui->instanceLabel->setVisible(false);
|
||||
ui->instance->setVisible(false);
|
||||
ui->meshFilterLabel->setVisible(true);
|
||||
ui->resetMeshFilterButton->setVisible(true);
|
||||
|
||||
ui->fixedVars->setVisible(true);
|
||||
ui->out1Table->setVisible(false);
|
||||
m_Containers[2]->setWindowTitle(tr("Mesh Input"));
|
||||
@@ -4994,6 +5269,11 @@ void BufferViewer::updateLabelsAndLayout()
|
||||
m_Containers[1]->layout()->addWidget(ui->out1Table);
|
||||
m_Containers[2]->layout()->addWidget(ui->out2Table);
|
||||
|
||||
ui->instanceLabel->setVisible(true);
|
||||
ui->instance->setVisible(true);
|
||||
ui->meshFilterLabel->setVisible(false);
|
||||
ui->resetMeshFilterButton->setVisible(false);
|
||||
|
||||
ui->fixedVars->setVisible(false);
|
||||
ui->out1Table->setVisible(true);
|
||||
m_Containers[0]->setWindowTitle(tr("VS Input"));
|
||||
@@ -5018,6 +5298,11 @@ void BufferViewer::updateLabelsAndLayout()
|
||||
m_Containers[1]->layout()->addWidget(ui->out1Table);
|
||||
m_Containers[2]->layout()->addWidget(ui->out2Table);
|
||||
|
||||
ui->instanceLabel->setVisible(true);
|
||||
ui->instance->setVisible(true);
|
||||
ui->meshFilterLabel->setVisible(false);
|
||||
ui->resetMeshFilterButton->setVisible(false);
|
||||
|
||||
ui->fixedVars->setVisible(false);
|
||||
ui->out1Table->setVisible(true);
|
||||
m_Containers[0]->setWindowTitle(tr("VS Input"));
|
||||
@@ -5267,6 +5552,7 @@ void BufferViewer::Reset()
|
||||
ClearModels();
|
||||
|
||||
updateLabelsAndLayout();
|
||||
SetMeshFilter(MeshFilter::None);
|
||||
|
||||
ui->fixedVars->clear();
|
||||
|
||||
@@ -5623,6 +5909,11 @@ void BufferViewer::on_setFormat_toggled(bool checked)
|
||||
reflection->constantBlocks[m_CBufferSlot.slot].variables, 0));
|
||||
}
|
||||
|
||||
void BufferViewer::on_resetMeshFilterButton_clicked()
|
||||
{
|
||||
SetMeshFilter(MeshFilter::None);
|
||||
}
|
||||
|
||||
void BufferViewer::processFormat(const QString &format)
|
||||
{
|
||||
// save scroll values now before we reset all the models
|
||||
@@ -6490,6 +6781,115 @@ void BufferViewer::on_viewIndex_valueChanged(int value)
|
||||
OnEventChanged(m_Ctx.CurEvent());
|
||||
}
|
||||
|
||||
void BufferViewer::SetMeshFilter(MeshFilter filter, uint32_t taskGroup, uint32_t meshGroup)
|
||||
{
|
||||
// calculate new scrolls manually to keep the same logical item selected
|
||||
m_Scrolls = new PopulateBufferData;
|
||||
FillScrolls(m_Scrolls);
|
||||
|
||||
{
|
||||
const BufferConfiguration &config1 = m_ModelOut1->getConfig();
|
||||
const BufferConfiguration &config2 = m_ModelOut2->getConfig();
|
||||
|
||||
// baseTaskRow is the first row in the mesh view for the start of the task with no mesh filter,
|
||||
// and baseMeshRow is the offset to the filtered mesh (if relevant). They could be identical
|
||||
const uint32_t prevBaseTaskRow = m_TaskFilterRowOffset;
|
||||
const uint32_t prevBaseMeshRow = m_MeshFilterRowOffset;
|
||||
|
||||
// if we're filtering directly to a task from none, we also have the prefix count we just have
|
||||
// to determine the base mesh
|
||||
uint32_t taskBaseMesh = 0;
|
||||
for(uint32_t i = 0; i < taskGroup && i < config1.taskSizes.size(); i++)
|
||||
taskBaseMesh += config1.taskSizes[i].x * config1.taskSizes[i].y * config1.taskSizes[i].z;
|
||||
|
||||
uint32_t newBaseTaskRow = 0, newBaseMeshRow = 0;
|
||||
if(filter == MeshFilter::None || config2.meshletVertexPrefixCounts.empty())
|
||||
{
|
||||
// if the new filter is none, then our new base row for both is 0
|
||||
newBaseTaskRow = newBaseMeshRow = 0;
|
||||
}
|
||||
else if(m_CurMeshFilter == MeshFilter::None && filter == MeshFilter::Mesh)
|
||||
{
|
||||
newBaseTaskRow = config2.meshletVertexPrefixCounts[taskBaseMesh];
|
||||
newBaseMeshRow = config2.meshletVertexPrefixCounts[meshGroup];
|
||||
}
|
||||
else if(m_CurMeshFilter == MeshFilter::None && filter == MeshFilter::TaskGroup)
|
||||
{
|
||||
newBaseTaskRow = newBaseMeshRow = config2.meshletVertexPrefixCounts[taskBaseMesh];
|
||||
}
|
||||
else if(m_CurMeshFilter == MeshFilter::TaskGroup && filter == MeshFilter::Mesh)
|
||||
{
|
||||
// the first complex case - if we're already filtered to a task and now we're filtering to a
|
||||
// mesh, we only have prefix counts relatively so look it up
|
||||
newBaseTaskRow = prevBaseTaskRow;
|
||||
newBaseMeshRow = prevBaseTaskRow + config2.meshletVertexPrefixCounts[meshGroup - taskBaseMesh];
|
||||
}
|
||||
else if(m_CurMeshFilter == MeshFilter::Mesh && filter == MeshFilter::TaskGroup)
|
||||
{
|
||||
// the second complex case - if we're already filtered to a *mesh* and now we're filtering
|
||||
// back to the task, we undo the previous per-mesh filter
|
||||
newBaseTaskRow = newBaseMeshRow = prevBaseTaskRow;
|
||||
|
||||
// only support filtering within the same group, not arbitrarily from one mesh in one task
|
||||
// group to a different task group
|
||||
Q_ASSERT(m_FilteredTaskGroup == taskGroup);
|
||||
}
|
||||
|
||||
const uint32_t prevBaseRow = prevBaseMeshRow;
|
||||
const uint32_t newBaseRow = newBaseMeshRow;
|
||||
|
||||
// when going to/from no filter, we just rebase by the base row and set the task row that we know directly
|
||||
if(m_CurMeshFilter == MeshFilter::None)
|
||||
{
|
||||
m_Scrolls->out1Vert = 0;
|
||||
m_Scrolls->out2Vert -= newBaseRow;
|
||||
}
|
||||
else if(filter == MeshFilter::None)
|
||||
{
|
||||
m_Scrolls->out1Vert = config1.taskOrMeshletOffset;
|
||||
m_Scrolls->out2Vert += prevBaseRow;
|
||||
}
|
||||
|
||||
// otherwise changing between task and mesh filter, we rebase based on the difference between
|
||||
// the number of meshes shown. The task filter doesn't have to change
|
||||
else if(m_CurMeshFilter == MeshFilter::TaskGroup && filter == MeshFilter::Mesh)
|
||||
{
|
||||
m_Scrolls->out1Vert = 0;
|
||||
m_Scrolls->out2Vert -= (newBaseRow - prevBaseRow);
|
||||
}
|
||||
else if(m_CurMeshFilter == MeshFilter::Mesh && filter == MeshFilter::TaskGroup)
|
||||
{
|
||||
m_Scrolls->out1Vert = 0;
|
||||
m_Scrolls->out2Vert += (prevBaseRow - newBaseRow);
|
||||
}
|
||||
|
||||
m_TaskFilterRowOffset = newBaseTaskRow;
|
||||
m_MeshFilterRowOffset = newBaseMeshRow;
|
||||
}
|
||||
|
||||
m_CurMeshFilter = filter;
|
||||
m_FilteredTaskGroup = taskGroup;
|
||||
m_FilteredMeshGroup = meshGroup;
|
||||
|
||||
switch(m_CurMeshFilter)
|
||||
{
|
||||
case MeshFilter::None:
|
||||
ui->meshFilterLabel->setText(tr("Current Range filter: None"));
|
||||
ui->resetMeshFilterButton->setEnabled(false);
|
||||
break;
|
||||
case MeshFilter::TaskGroup:
|
||||
ui->meshFilterLabel->setText(tr("Current Range filter: Single Task Group"));
|
||||
ui->resetMeshFilterButton->setEnabled(true);
|
||||
break;
|
||||
case MeshFilter::Mesh:
|
||||
ui->meshFilterLabel->setText(tr("Current Range filter: Single Meshlet"));
|
||||
ui->resetMeshFilterButton->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
OnEventChanged(m_Ctx.CurEvent());
|
||||
}
|
||||
|
||||
void BufferViewer::on_rowOffset_valueChanged(int value)
|
||||
{
|
||||
if(!m_MeshView && m_ModelIn->getConfig().unclampedNumRows > 0)
|
||||
|
||||
@@ -141,6 +141,7 @@ private slots:
|
||||
void on_axisMappingCombo_currentIndexChanged(int index);
|
||||
void on_axisMappingButton_clicked();
|
||||
void on_setFormat_toggled(bool checked);
|
||||
void on_resetMeshFilterButton_clicked();
|
||||
|
||||
// manual slots
|
||||
void render_mouseMove(QMouseEvent *e);
|
||||
@@ -210,6 +211,8 @@ private:
|
||||
|
||||
void FillScrolls(PopulateBufferData *bufdata);
|
||||
|
||||
void GetIndicesForMeshRow(uint32_t row, uint32_t &taskIndex, uint32_t &meshletIndex);
|
||||
|
||||
void UI_ResetArcball();
|
||||
|
||||
// data from raw buffer view
|
||||
@@ -263,6 +266,17 @@ private:
|
||||
// (mesh out) and [2] being in
|
||||
QWidget *m_Containers[3] = {};
|
||||
|
||||
enum class MeshFilter
|
||||
{
|
||||
None,
|
||||
TaskGroup,
|
||||
Mesh,
|
||||
};
|
||||
|
||||
MeshFilter m_CurMeshFilter = MeshFilter::None;
|
||||
uint32_t m_FilteredTaskGroup = ~0U, m_FilteredMeshGroup = ~0U;
|
||||
uint32_t m_TaskFilterRowOffset = 0, m_MeshFilterRowOffset = 0;
|
||||
|
||||
PopulateBufferData *m_Scrolls = NULL;
|
||||
|
||||
QPoint m_Scroll[4];
|
||||
@@ -306,6 +320,9 @@ private:
|
||||
QAction *m_ExportCSV = NULL;
|
||||
QAction *m_ExportBytes = NULL;
|
||||
QAction *m_DebugVert = NULL;
|
||||
QAction *m_FilterMesh = NULL;
|
||||
QAction *m_RemoveFilter = NULL;
|
||||
QAction *m_GotoTask = NULL;
|
||||
|
||||
RDSpinBox64 *byteRangeStart = NULL;
|
||||
RDSpinBox64 *byteRangeLength = NULL;
|
||||
@@ -322,6 +339,8 @@ private:
|
||||
void SetupMeshView();
|
||||
void SetupRawView();
|
||||
|
||||
void SetMeshFilter(MeshFilter filter, uint32_t taskGroup = ~0U, uint32_t meshGroup = ~0U);
|
||||
|
||||
void stageRowMenu(MeshDataStage stage, QMenu *menu, const QPoint &pos);
|
||||
void meshHeaderMenu(MeshDataStage stage, const QPoint &pos);
|
||||
|
||||
|
||||
@@ -988,6 +988,24 @@ Enter 0.0 to use automatic/guessed value derived from data.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="meshFilterLabel">
|
||||
<property name="text">
|
||||
<string>Current Range filter: None</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="resetMeshFilterButton">
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/arrow_undo.png</normaloff>:/arrow_undo.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="byteRangeLine">
|
||||
<property name="orientation">
|
||||
|
||||
Reference in New Issue
Block a user