From cb0df2c84457f5f71c6435dd2ecccc47bb36938b Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 8 Jun 2018 18:29:02 +0100 Subject: [PATCH] Combine together structs/arrays in HLSL locals panel --- qrenderdoc/Widgets/Extended/RDTreeWidget.cpp | 35 +++--- qrenderdoc/Widgets/Extended/RDTreeWidget.h | 6 +- qrenderdoc/Windows/ShaderViewer.cpp | 120 ++++++++++++++++++- qrenderdoc/Windows/ShaderViewer.h | 1 + 4 files changed, 142 insertions(+), 20 deletions(-) diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp index 4d0f9acfc..b1b277790 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp @@ -79,13 +79,12 @@ public: return createIndex(row, column, par->m_children[row]); } - void beginAddChild(RDTreeWidgetItem *item) + void beginInsertChild(RDTreeWidgetItem *item, int index) { - QModelIndex index = indexForItem(item, 0); - beginInsertRows(index, item->childCount(), item->childCount()); + beginInsertRows(indexForItem(item, 0), index, index); } - void endAddChild(RDTreeWidgetItem *item) { endInsertRows(); } + void endInsertChild(RDTreeWidgetItem *item) { endInsertRows(); } void beginRemoveChildren(RDTreeWidgetItem *parent, int first, int last) { beginRemoveRows(indexForItem(parent, 0), first, last); @@ -420,6 +419,11 @@ void RDTreeWidgetItem::setData(int column, int role, const QVariant &value) } void RDTreeWidgetItem::addChild(RDTreeWidgetItem *item) +{ + insertChild(m_children.count(), item); +} + +void RDTreeWidgetItem::insertChild(int index, RDTreeWidgetItem *item) { int colCount = item->m_text.count(); @@ -445,13 +449,13 @@ void RDTreeWidgetItem::addChild(RDTreeWidgetItem *item) item->m_data->resize(qMax(item->m_data->count(), colCount)); if(m_widget) - m_widget->beginAddChild(this); + m_widget->beginInsertChild(this, index); // add to our list of children - m_children.push_back(item); + m_children.insert(index, item); if(m_widget) - m_widget->endAddChild(this); + m_widget->endInsertChild(this, index); } void RDTreeWidgetItem::setWidget(RDTreeWidget *widget) @@ -783,8 +787,8 @@ void RDTreeWidget::endUpdate() if(m_queuedChildren) { - m_model->beginAddChild(m_queuedItem); - m_model->endAddChild(m_queuedItem); + m_model->beginInsertChild(m_queuedItem, m_queuedItem->childCount()); + m_model->endInsertChild(m_queuedItem); } if(!roles.isEmpty()) @@ -1179,7 +1183,7 @@ void RDTreeWidget::itemDataChanged(RDTreeWidgetItem *item, int column, int role) emit itemChanged(item, column); } -void RDTreeWidget::beginAddChild(RDTreeWidgetItem *item) +void RDTreeWidget::beginInsertChild(RDTreeWidgetItem *item, int index) { if(m_queueUpdates) { @@ -1190,8 +1194,7 @@ void RDTreeWidget::beginAddChild(RDTreeWidgetItem *item) m_queuedItem = item; // make an update of row 0. This will be a bit pessimistic if there are later data changes // in a later row, but we're generally only changing data *or* adding children, not both, - // and - // in either case this is primarily about batching updates not providing a minimal update + // and in either case this is primarily about batching updates not providing a minimal update // set m_lowestIndex = qMakePair(0, 0); m_highestIndex = qMakePair(0, m_headers.count() - 1); @@ -1211,13 +1214,13 @@ void RDTreeWidget::beginAddChild(RDTreeWidgetItem *item) } else { - m_model->beginAddChild(item); + m_model->beginInsertChild(item, index); } } -void RDTreeWidget::endAddChild(RDTreeWidgetItem *item) +void RDTreeWidget::endInsertChild(RDTreeWidgetItem *item, int index) { - // work is all done in beginAddChild + // work is all done in beginInsertChild if(!m_queueUpdates) - m_model->endAddChild(item); + m_model->endInsertChild(item); } \ No newline at end of file diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.h b/qrenderdoc/Widgets/Extended/RDTreeWidget.h index a51f73d8e..0587aa838 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.h +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.h @@ -41,6 +41,7 @@ public: void setData(int column, int role, const QVariant &value); void addChild(RDTreeWidgetItem *item); + void insertChild(int index, RDTreeWidgetItem *child); // the data above requires allocating a bunch of vectors since it's stored per-column. Where // possible, just use this single per-item tag @@ -63,6 +64,7 @@ public: RDTreeWidgetItem *takeChild(int index); void removeChild(RDTreeWidgetItem *child); void clear(); + inline int dataCount() const { return m_text.count(); } inline int childCount() const { return m_children.count(); } inline RDTreeWidgetItem *parent() const { return m_parent; } inline RDTreeWidget *treeWidget() const { return m_widget; } @@ -295,8 +297,8 @@ private: void setModel(QAbstractItemModel *model) override {} void itemDataChanged(RDTreeWidgetItem *item, int column, int role); - void beginAddChild(RDTreeWidgetItem *item); - void endAddChild(RDTreeWidgetItem *item); + void beginInsertChild(RDTreeWidgetItem *item, int index); + void endInsertChild(RDTreeWidgetItem *item, int index); friend class RDTreeWidgetModel; friend class RDTreeWidgetItem; diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 676a07f65..46201aed6 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -1380,6 +1380,114 @@ void ShaderViewer::addFileList() list, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); } +void ShaderViewer::combineStructures(RDTreeWidgetItem *root) +{ + RDTreeWidgetItem temp; + + // we perform a filter moving from root to temp. At each point we check the node: + // * if the node has no struct or array prefix, it gets moved + // * if the node does have a prefix, we sweep finding all matching elements with the same prefix, + // strip the prefix off them and make a combined node, then recurse to combine anything + // underneath. We aren't greedy in picking prefixes so this should generate a struct/array tree. + // * in the event that a node has no matching elements we move it across as if it had no prefix. + // * we iterate from last to first, because when combining elements that may be spread out in the + // list of children, we want to combine up to the position of the last item, not the position of + // the first. + + for(int c = root->childCount() - 1; c >= 0;) + { + RDTreeWidgetItem *child = root->takeChild(c); + c--; + + QString name = child->text(0); + + int dotIndex = name.indexOf(QLatin1Char('.')); + int arrIndex = name.indexOf(QLatin1Char('[')); + + // if this node doesn't have any segments, just move it across. + if(dotIndex < 0 && arrIndex < 0) + { + temp.insertChild(0, child); + continue; + } + + // store the index of the first separator + int sepIndex = dotIndex; + bool isArray = false; + if(sepIndex == -1 || (arrIndex > 0 && arrIndex < sepIndex)) + { + sepIndex = arrIndex; + isArray = true; + } + + // we have a valid node to match against, record the prefix (including separator character) + QString prefix = name.mid(0, sepIndex + 1); + + QVector matches = {child}; + + // iterate down from the next item + for(int n = c; n >= 0; n--) + { + RDTreeWidgetItem *testNode = root->child(n); + + QString testName = testNode->text(0); + + QString testprefix = testName.mid(0, sepIndex + 1); + + // no match - continue + if(testprefix != prefix) + continue; + + // match, take this child + matches.push_back(root->takeChild(n)); + + // also decrement c since we're taking a child ahead of where that loop will go. + c--; + } + + // no other matches with the same prefix, just move across + if(matches.count() == 1) + { + temp.insertChild(0, child); + continue; + } + + // sort the children by name + std::sort(matches.begin(), matches.end(), + [](const RDTreeWidgetItem *a, const RDTreeWidgetItem *b) { + return a->text(0) < b->text(0); + }); + + // create a new parent with just the prefix + QVariantList values = {isArray ? name : name.mid(0, sepIndex)}; + for(int i = 1; i < child->dataCount(); i++) + values.push_back(QVariant()); + RDTreeWidgetItem *parent = new RDTreeWidgetItem(values); + + // add all the children (stripping the prefix from their name) + for(RDTreeWidgetItem *item : matches) + { + if(!isArray) + item->setText(0, item->text(0).mid(sepIndex + 1)); + parent->addChild(item); + } + + // recurse and combine members of this object if a struct + if(!isArray) + combineStructures(parent); + + // now add to the list + temp.insertChild(0, parent); + } + + if(root->childCount() > 0) + qCritical() << "Some objects left on root!"; + + // move all the children back from the temp object into the parameter + while(temp.childCount() > 0) + root->addChild(temp.takeChild(0)); +} + void ShaderViewer::updateDebugging() { if(!m_Trace || m_CurrentStep < 0 || m_CurrentStep >= m_Trace->states.count()) @@ -1619,6 +1727,8 @@ void ShaderViewer::updateDebugging() const QString xyzw = lit("xyzw"); + RDTreeWidgetItem fakeroot; + for(size_t lidx = 0; lidx < state.locals.size(); lidx++) { // iterate in reverse order, so newest locals tend to end up on top @@ -1646,7 +1756,7 @@ void ShaderViewer::updateDebugging() else { if(l.rows > 1) - typeName += QFormatStr("%1x%1").arg(l.rows).arg(l.columns); + typeName += QFormatStr("%1x%2").arg(l.rows).arg(l.columns); else typeName += QString::number(l.columns); @@ -1733,8 +1843,14 @@ void ShaderViewer::updateDebugging() } } - ui->locals->addTopLevelItem(node); + fakeroot.addChild(node); } + + // recursively combine nodes with the same prefix together + combineStructures(&fakeroot); + + while(fakeroot.childCount() > 0) + ui->locals->addTopLevelItem(fakeroot.takeChild(0)); } if(ui->registers->topLevelItemCount() == 0) diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 8bc10bf3a..9a8b6f727 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -251,4 +251,5 @@ private: QString stringRep(const ShaderVariable &var, bool useType); RDTreeWidgetItem *makeResourceRegister(const Bindpoint &bind, uint32_t idx, const BoundResource &ro, const ShaderResource &resources); + void combineStructures(RDTreeWidgetItem *root); };