From 0f019eafb9c47617570fd4dc1f7a0c87be236ed2 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 16 Apr 2025 15:48:21 +0100 Subject: [PATCH] Add option to make shader search paths non-recursive * For very large shader symbol stores especially those on network drives, the bad behaviour that PIX has to recursively search all possible subdirectories and enumerate all files can be really slow. Most of the time a file is identified by its hash filename and looked up directly - if that isn't a hit, in many cases users would rather a fast exit to having no symbols. --- .../Code/Interface/PersistantConfig.cpp | 12 - qrenderdoc/Widgets/OrderedListEditor.cpp | 219 ++++++++++++------ qrenderdoc/Widgets/OrderedListEditor.h | 45 +++- qrenderdoc/Windows/Dialogs/ConfigEditor.cpp | 6 +- qrenderdoc/Windows/Dialogs/SettingsDialog.cpp | 46 +++- renderdoc/core/core.cpp | 5 +- .../driver/shaders/dxbc/dxbc_container.cpp | 8 + 7 files changed, 243 insertions(+), 98 deletions(-) diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 850ed3f45..75043648a 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -303,18 +303,6 @@ void PersistantConfig::applyValues(const QVariantMap &values) debug->AddAndOwnChild(makeSDString("$el"_lit, searchPaths[i])); } - if(settings.contains(lit("d3d12ShaderDebugging"))) - { - RENDERDOC_SetConfigSetting("D3D12_ShaderDebugging")->data.basic.b = - settings[lit("d3d12ShaderDebugging")].toBool(); - } - - if(settings.contains(lit("vulkanShaderDebugging"))) - { - RENDERDOC_SetConfigSetting("Vulkan_ShaderDebugging")->data.basic.b = - settings[lit("vulkanShaderDebugging")].toBool(); - } - saveConfig = true; } diff --git a/qrenderdoc/Widgets/OrderedListEditor.cpp b/qrenderdoc/Widgets/OrderedListEditor.cpp index d524df0b9..5dd83fa7f 100644 --- a/qrenderdoc/Widgets/OrderedListEditor.cpp +++ b/qrenderdoc/Widgets/OrderedListEditor.cpp @@ -23,18 +23,20 @@ ******************************************************************************/ #include "OrderedListEditor.h" +#include #include #include #include #include "Code/QRDUtils.h" #include "Code/Resources.h" -OrderedListEditor::OrderedListEditor(const QString &itemName, ItemButton mode, QWidget *parent) +OrderedListEditor::OrderedListEditor(const QString &itemName, OrderedItemExtras extras, + const CustomProp &prop, QWidget *parent) : RDTableWidget(parent) { setFont(Formatter::PreferredFont()); - m_ButtonMode = mode; + m_Prop = prop; setDragEnabled(true); setDragDropOverwriteMode(false); @@ -49,30 +51,45 @@ OrderedListEditor::OrderedListEditor(const QString &itemName, ItemButton mode, Q horizontalHeader()->setMinimumSectionSize(50); verticalHeader()->setHighlightSections(false); - if(m_ButtonMode == ItemButton::None) - { - setColumnCount(1); - setHorizontalHeaderLabels({itemName}); - horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - } - else - { - QStringList labels; - labels << itemName; - switch(m_ButtonMode) - { - case ItemButton::None: labels << lit("????"); break; - case ItemButton::BrowseFile: - case ItemButton::BrowseFolder: labels << tr("Browse"); break; - case ItemButton::Delete: labels << tr("Delete"); break; - } - setColumnCount(2); - setHorizontalHeaderLabels(labels); + QStringList labels; + int columnCount = 1; + labels << itemName; - horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + if(!prop.name.isEmpty()) + { + columnCount++; + labels << prop.name; } + if(extras & OrderedItemExtras::BrowseFile) + { + columnCount++; + labels << tr("Browse"); + m_Extras.push_back(OrderedItemExtras::BrowseFile); + } + else if(extras & OrderedItemExtras::BrowseFolder) + { + columnCount++; + labels << tr("Browse"); + m_Extras.push_back(OrderedItemExtras::BrowseFolder); + } + if(extras & OrderedItemExtras::Delete) + { + columnCount++; + labels << tr("Delete"); + m_Extras.push_back(OrderedItemExtras::Delete); + } + + setColumnCount(columnCount); + setHorizontalHeaderLabels(labels); + + horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + for(int i = 1; i < columnCount; i++) + horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents); + + if(!prop.name.isEmpty()) + horizontalHeaderItem(1)->setToolTip(prop.tooltip); + QObject::connect(this, &RDTableWidget::cellChanged, this, &OrderedListEditor::cellChanged); } @@ -80,22 +97,40 @@ OrderedListEditor::~OrderedListEditor() { } -QToolButton *OrderedListEditor::makeButton() +QWidget *OrderedListEditor::makeCellWidget(int row, OrderedItemExtras extra) { - QToolButton *ret = new QToolButton(this); - - if(m_ButtonMode == ItemButton::Delete) + if(extra == OrderedItemExtras::Delete) + { + QToolButton *ret = new QToolButton(this); + ret->setAutoRaise(true); ret->setIcon(Icons::del()); - else + QObject::connect(ret, &QToolButton::clicked, [this, row, extra]() { extraClicked(row, extra); }); + + return ret; + } + else if(extra == OrderedItemExtras::BrowseFile || extra == OrderedItemExtras::BrowseFolder) + { + QToolButton *ret = new QToolButton(this); + ret->setAutoRaise(true); ret->setIcon(Icons::folder_page_white()); - ret->setAutoRaise(true); + QObject::connect(ret, &QToolButton::clicked, [this, row, extra]() { extraClicked(row, extra); }); - QObject::connect(ret, &QToolButton::clicked, this, &OrderedListEditor::buttonActivate); - - return ret; + return ret; + } + else if(extra == OrderedItemExtras::CustomProp) + { + QCheckBox *ret = new QCheckBox(this); + if(m_Prop.defaultValue) + ret->setChecked(true); + return ret; + } + else + { + return NULL; + } } -void OrderedListEditor::setItems(const QStringList &strings) +void OrderedListEditor::setItemsAndProp(const QStringList &strings, const QList &prop) { setUpdatesEnabled(false); clearContents(); @@ -106,8 +141,28 @@ void OrderedListEditor::setItems(const QStringList &strings) { setItem(i, 0, new QTableWidgetItem(strings[i])); - if(m_ButtonMode != ItemButton::None) - setCellWidget(i, 1, makeButton()); + if(m_Prop.valid()) + { + QWidget *w = makeCellWidget(i, OrderedItemExtras::CustomProp); + if(i < prop.count()) + { + QCheckBox *c = qobject_cast(w); + if(c) + c->setChecked(prop[i]); + } + w->setToolTip(m_Prop.tooltip); + + QWidget *wrapperWidget = new QWidget(); + QHBoxLayout *l = new QHBoxLayout(); + l->setAlignment(Qt::AlignCenter); + l->addWidget(w); + l->setContentsMargins(QMargins(0, 0, 0, 0)); + wrapperWidget->setLayout(l); + setCellWidget(i, 1, wrapperWidget); + } + + for(int c = 0; c < m_Extras.size(); c++) + setCellWidget(i, c + firstExtraColumn(), makeCellWidget(i, m_Extras[c])); } // if we added any strings above the new item row was automatically @@ -116,8 +171,8 @@ void OrderedListEditor::setItems(const QStringList &strings) addNewItemRow(); resizeColumnToContents(0); - if(m_ButtonMode != ItemButton::None) - resizeColumnToContents(1); + for(int c = 0; c < m_Extras.size(); c++) + resizeColumnToContents(c + firstExtraColumn()); setUpdatesEnabled(true); } @@ -133,13 +188,30 @@ void OrderedListEditor::addNewItemRow() item->setFlags(item->flags() & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled)); setItem(rowCount() - 1, 0, item); - if(m_ButtonMode != ItemButton::None) + { + QWidget *w = makeCellWidget(rowCount() - 1, OrderedItemExtras::CustomProp); + QCheckBox *c = qobject_cast(w); + if(c) + c->setChecked(m_Prop.defaultValue); + w->setToolTip(m_Prop.tooltip); + + QWidget *wrapperWidget = new QWidget(); + QHBoxLayout *l = new QHBoxLayout(); + l->setAlignment(Qt::AlignCenter); + l->addWidget(w); + l->setContentsMargins(QMargins(0, 0, 0, 0)); + wrapperWidget->setLayout(l); + setCellWidget(rowCount() - 1, 1, wrapperWidget); + } + + for(int c = 0; c < m_Extras.size(); c++) { item = new QTableWidgetItem(QString()); item->setFlags(item->flags() & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled)); - setItem(rowCount() - 1, 1, item); + setItem(rowCount() - 1, c + firstExtraColumn(), item); - setCellWidget(rowCount() - 1, 1, makeButton()); + setCellWidget(rowCount() - 1, c + firstExtraColumn(), + makeCellWidget(rowCount() - 1, m_Extras[c])); } } @@ -157,6 +229,23 @@ QStringList OrderedListEditor::getItems() return ret; } +QList OrderedListEditor::getItemProps() +{ + QList ret; + + int count = rowCount(); + // don't include the last 'new item' entry + if(allowAddition()) + count--; + for(int i = 0; i < count; i++) + { + QCheckBox *c = cellWidget(i, 1)->findChild(); + ret << (c && c->isChecked()); + } + + return ret; +} + void OrderedListEditor::cellChanged(int row, int column) { // hack :(. Assume this will only be hit on single UI thread. @@ -174,8 +263,8 @@ void OrderedListEditor::cellChanged(int row, int column) { // enable dragging item(row, 0)->setFlags(item(row, 0)->flags() | (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled)); - if(m_ButtonMode != ItemButton::None) - delete takeItem(row, 1); + for(int c = 0; c < m_Extras.size(); c++) + delete takeItem(row, c + firstExtraColumn()); addNewItemRow(); } @@ -189,34 +278,24 @@ void OrderedListEditor::cellChanged(int row, int column) recurse = false; } -void OrderedListEditor::buttonActivate() +void OrderedListEditor::extraClicked(int row, OrderedItemExtras extra) { - QWidget *tool = qobject_cast(QObject::sender()); - - if(tool) + if(extra == OrderedItemExtras::Delete) { - for(int i = 0; i < rowCount(); i++) - { - QWidget *rowButton = cellWidget(i, 1); - if(rowButton == tool) - { - if(m_ButtonMode == ItemButton::Delete) - { - this->removeRow(i); - return; - } - - QString sel; - if(m_ButtonMode == ItemButton::BrowseFolder) - sel = RDDialog::getExistingDirectory(this, tr("Browse for a folder")); - else if(m_ButtonMode == ItemButton::BrowseFile) - sel = RDDialog::getOpenFileName(this, tr("Browse for a file")); - - if(!sel.isEmpty()) - item(i, 0)->setText(sel); - } - } + // don't delete the last 'new item' entry + if(!allowAddition() || row != rowCount() - 1) + removeRow(row); + return; } + + QString sel; + if(extra == OrderedItemExtras::BrowseFolder) + sel = RDDialog::getExistingDirectory(this, tr("Browse for a folder")); + else if(extra == OrderedItemExtras::BrowseFile) + sel = RDDialog::getOpenFileName(this, tr("Browse for a file")); + + if(!sel.isEmpty()) + item(row, 0)->setText(sel); } void OrderedListEditor::keyPressEvent(QKeyEvent *event) @@ -228,7 +307,11 @@ void OrderedListEditor::keyPressEvent(QKeyEvent *event) row = selectionModel()->selectedIndexes()[0].row(); if(row >= 0) - removeRow(row); + { + // don't delete the last 'new item' entry + if(!allowAddition() || row != rowCount() - 1) + removeRow(row); + } } RDTableWidget::keyPress(event); diff --git a/qrenderdoc/Widgets/OrderedListEditor.h b/qrenderdoc/Widgets/OrderedListEditor.h index 9abe18224..7b4ebc7df 100644 --- a/qrenderdoc/Widgets/OrderedListEditor.h +++ b/qrenderdoc/Widgets/OrderedListEditor.h @@ -28,24 +28,46 @@ class QToolButton; -enum class ItemButton +enum class OrderedItemExtras { - None, - BrowseFolder, - BrowseFile, - Delete, + None = 0x0, + BrowseFolder = 0x1, + BrowseFile = 0x2, + Delete = 0x4, + CustomProp = 0x8, }; +constexpr inline OrderedItemExtras operator|(OrderedItemExtras a, OrderedItemExtras b) +{ + return OrderedItemExtras(int(a) | int(b)); +} + +constexpr inline bool operator&(OrderedItemExtras a, OrderedItemExtras b) +{ + return int(a) & int(b); +} + class OrderedListEditor : public RDTableWidget { Q_OBJECT public: - explicit OrderedListEditor(const QString &itemName, ItemButton button, QWidget *parent = 0); + struct CustomProp + { + QString name, tooltip; + bool defaultValue; + + bool valid() const { return !name.isEmpty(); } + }; + + explicit OrderedListEditor(const QString &itemName, OrderedItemExtras extras, + const CustomProp &prop = {}, QWidget *parent = 0); ~OrderedListEditor(); - void setItems(const QStringList &strings); + void setItemsAndProp(const QStringList &strings, const QList &props); + void setItems(const QStringList &strings) { setItemsAndProp(strings, {}); } QStringList getItems(); + QList getItemProps(); bool allowAddition() { return m_allowAddition; } void setAllowAddition(bool allow) { m_allowAddition = allow; } @@ -53,15 +75,18 @@ public: private slots: // manual slots void cellChanged(int row, int column); - void buttonActivate(); + void extraClicked(int row, OrderedItemExtras extra); private: void keyPressEvent(QKeyEvent *e) override; - ItemButton m_ButtonMode; + int firstExtraColumn() { return m_Prop.valid() ? 2 : 1; } + + QList m_Extras; + CustomProp m_Prop; bool m_allowAddition = true; void addNewItemRow(); - QToolButton *makeButton(); + QWidget *makeCellWidget(int row, OrderedItemExtras extra); }; diff --git a/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp b/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp index ec9e008bd..f077b5648 100644 --- a/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp +++ b/qrenderdoc/Windows/Dialogs/ConfigEditor.cpp @@ -565,12 +565,12 @@ QWidget *SettingDelegate::createEditor(QWidget *parent, const QStyleOptionViewIt listEditor.setWindowTitle(tr("Edit values of %1").arg(QString(settingName))); listEditor.setWindowFlags(listEditor.windowFlags() & ~Qt::WindowContextHelpButtonHint); - ItemButton mode = ItemButton::None; + OrderedItemExtras mode = OrderedItemExtras::None; if(QString(o->name).contains(lit("DirPath"), Qt::CaseSensitive)) - mode = ItemButton::BrowseFolder; + mode = OrderedItemExtras::BrowseFolder; else if(QString(o->name).contains(lit("Path"), Qt::CaseSensitive)) - mode = ItemButton::BrowseFile; + mode = OrderedItemExtras::BrowseFile; OrderedListEditor list(tr("Entry"), mode); diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 88d7862bb..ebc016517 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -618,7 +618,15 @@ void SettingsDialog::on_chooseSearchPaths_clicked() listEditor.setWindowTitle(tr("Shader debug info search paths")); listEditor.setWindowFlags(listEditor.windowFlags() & ~Qt::WindowContextHelpButtonHint); - OrderedListEditor list(tr("Search Path"), ItemButton::BrowseFolder); + OrderedListEditor::CustomProp customProp = { + tr("Recursive"), + tr("Recursively search through all subdirectories" + "under this path to find matching debug files"), + true, + }; + + OrderedListEditor list(tr("Search Path"), + OrderedItemExtras::BrowseFolder | OrderedItemExtras::Delete, customProp); QVBoxLayout layout; QDialogButtonBox okCancel; @@ -630,21 +638,37 @@ void SettingsDialog::on_chooseSearchPaths_clicked() QObject::connect(&okCancel, &QDialogButtonBox::rejected, &listEditor, &QDialog::reject); listEditor.setLayout(&layout); + listEditor.resize(750, 500); const SDObject *getPaths = RENDERDOC_GetConfigSetting("DXBC.Debug.SearchDirPaths"); QStringList items; + QList recursive; for(const SDObject *c : *getPaths) + { items << c->data.str; + recursive << true; + } - list.setItems(items); + const SDObject *getLimitedPaths = + RENDERDOC_GetConfigSetting("Replay.Shader.LimitedSearchDirPaths"); + + for(const SDObject *c : *getLimitedPaths) + { + int idx = items.indexOf(c->data.str); + if(idx >= 0) + recursive[idx] = false; + } + + list.setItemsAndProp(items, recursive); int res = RDDialog::show(&listEditor); if(res) { items = list.getItems(); + recursive = list.getItemProps(); SDObject *setPaths = RENDERDOC_SetConfigSetting("DXBC.Debug.SearchDirPaths"); @@ -654,6 +678,20 @@ void SettingsDialog::on_chooseSearchPaths_clicked() for(int i = 0; i < items.size(); i++) setPaths->AddAndOwnChild(makeSDString("$el"_lit, items[i])); + SDObject *setLimitedPaths = RENDERDOC_SetConfigSetting("Replay.Shader.LimitedSearchDirPaths"); + + QStringList limited; + + for(int i = 0; i < recursive.count() && i < items.count(); i++) + if(recursive[i] == false) + limited << items[i]; + + setLimitedPaths->DeleteChildren(); + setLimitedPaths->ReserveChildren(limited.size()); + + for(int i = 0; i < limited.size(); i++) + setLimitedPaths->AddAndOwnChild(makeSDString("$el"_lit, limited[i])); + RENDERDOC_SaveConfigSettings(); } } @@ -665,7 +703,7 @@ void SettingsDialog::on_chooseIgnores_clicked() listEditor.setWindowTitle(tr("Ignored DLLs for callstack symbol resolution")); listEditor.setWindowFlags(listEditor.windowFlags() & ~Qt::WindowContextHelpButtonHint); - OrderedListEditor list(tr("Ignored DLL"), ItemButton::Delete); + OrderedListEditor list(tr("Ignored DLL"), OrderedItemExtras::Delete); list.setAllowAddition(false); @@ -761,7 +799,7 @@ void SettingsDialog::on_TextureViewer_ChooseShaderDirectories_clicked() listEditor.setWindowTitle(tr("Custom shaders search directories")); listEditor.setWindowFlags(listEditor.windowFlags() & ~Qt::WindowContextHelpButtonHint); - OrderedListEditor list(tr("Shaders Directory"), ItemButton::BrowseFolder); + OrderedListEditor list(tr("Shaders Directory"), OrderedItemExtras::BrowseFolder); QVBoxLayout layout; QDialogButtonBox okCancel; diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index a831494ff..c010bea88 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -63,7 +63,10 @@ RDOC_CONFIG(bool, Replay_Debug_SingleThreadedCompilation, false, // this is declared centrally so it can be shared with any backend - the name is a misnomer but kept // for backwards compatibility reasons. RDOC_CONFIG(rdcarray, DXBC_Debug_SearchDirPaths, {}, - "Paths to search for separated shader debug PDBs."); + "Paths to search for separated shader debug PDBs, including all types of paths."); +RDOC_CONFIG(rdcarray, Replay_Shader_LimitedSearchDirPaths, {}, + "Companion array to DXBC.Debug.SearchDirPaths - listing paths which should not be " + "searched exhaustively but only used for simple lookups."); void LogReplayOptions(const ReplayOptions &opts) { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index 3e5b46cf5..963b1f4f8 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -41,6 +41,7 @@ // this is extern so that it can be shared with vulkan RDOC_EXTERN_CONFIG(rdcarray, DXBC_Debug_SearchDirPaths); +RDOC_EXTERN_CONFIG(rdcarray, Replay_Shader_LimitedSearchDirPaths); namespace { @@ -93,9 +94,16 @@ void CacheSearchDirDebugPaths() return; rdcarray searchPaths = DXBC_Debug_SearchDirPaths(); + rdcarray limitedSearchPaths = Replay_Shader_LimitedSearchDirPaths(); for(const rdcstr &base : searchPaths) { + if(limitedSearchPaths.contains(base)) + { + RDCLOG("Not recursing to enumerate files under %s", base.c_str()); + continue; + } + size_t sz = cachedDebugFilesLookup.size(); CacheSearchDirDebugPaths(base); RDCLOG("Recursively enumerated all files under %s, found %zu files", base.c_str(),