Add configuration system for core renderdoc module

* This allows persistent config storage and registering tweak variables that
  works independent of the UI's configuration.
* Config vars can be debug only, which means they will be compiled out in stable
  version releases. This allows for debug-logging tweaks that are available in
  all builds (including nightly builds) for diagnostic purposes, but have zero
  overhead in stable releases.
* Variables have a loose hierarchy defined with _ or . to separate nodes.
This commit is contained in:
baldurk
2020-03-18 12:11:09 +00:00
parent c0e84875f3
commit d408f77241
41 changed files with 2123 additions and 322 deletions
+104 -57
View File
@@ -153,6 +153,16 @@ bool PersistantConfig::Serialize(const rdcstr &filename)
return false;
}
struct LegacyData
{
QString _Android_SDKPath;
QString _Android_JDKPath;
uint32_t _Android_MaxConnectTimeout = 30;
bool _ExternalTool_RGPIntegration = false;
bool _ShaderViewer_FriendlyNaming = true;
QVariantMap _ConfigSettings;
};
QVariantMap PersistantConfig::storeValues() const
{
QVariantMap ret;
@@ -167,6 +177,15 @@ QVariantMap PersistantConfig::storeValues() const
CONFIG_SETTINGS()
// store any legacy values even though we don't need them
ret[lit("Android_SDKPath")] = m_Legacy->_Android_SDKPath;
ret[lit("Android_JDKPath")] = m_Legacy->_Android_JDKPath;
ret[lit("Android_MaxConnectTimeout")] = m_Legacy->_Android_MaxConnectTimeout;
ret[lit("ExternalTool_RGPIntegration")] = m_Legacy->_ExternalTool_RGPIntegration;
ret[lit("ShaderViewer_FriendlyNaming")] = m_Legacy->_ShaderViewer_FriendlyNaming;
ret[lit("ConfigSettings")] = m_Legacy->_ConfigSettings;
return ret;
}
@@ -197,6 +216,85 @@ void PersistantConfig::applyValues(const QVariantMap &values)
// apply reasonable bounds to font scale to avoid invalid values
// 25% - 400%
Font_GlobalScale = qBound(0.25f, Font_GlobalScale, 4.0f);
// port old values that were saved here but are now saved in core.
// We only want to do this once, but we want to leave these values in the config to allow for
// people running old versions after running a new version - we don't want to remove all of their
// settings yet.
// So what we do is store an extra setting indicating that it's been ported - if that setting is
// present we just store/save them blindly. This does mean they can change but we won't re-port
// them.
// After the new config ships in a stable release we can remove this as we don't generally support
// forwards compatibility, only backwards compatibility, so it will be OK to drop the config
// settings.
bool processed = false;
if(values.contains(lit("ConfigSettings")) &&
values[lit("ConfigSettings")].toMap().contains(lit("modern.config.ported")))
processed = true;
bool saveConfig = false;
#define CORE_SETTING(variantType, oldName, newName, data) \
if(values.contains(lit(#oldName))) \
{ \
m_Legacy->_##oldName = values[lit(#oldName)].value<variantType>(); \
if(!processed) \
{ \
SDObject *setting = RENDERDOC_SetConfigSetting(newName); \
if(setting) \
setting->data = m_Legacy->_##oldName; \
saveConfig = true; \
} \
}
CORE_SETTING(QString, Android_SDKPath, "Android.SDKDirPath", data.str);
CORE_SETTING(QString, Android_JDKPath, "Android.JDKDirPath", data.str);
CORE_SETTING(uint32_t, Android_MaxConnectTimeout, "Android.MaxConnectTimeout", data.basic.u);
CORE_SETTING(bool, ExternalTool_RGPIntegration, "AMD.RGP.Enable", data.basic.b);
CORE_SETTING(bool, ShaderViewer_FriendlyNaming, "DXBC.Disassembly.FriendlyNaming", data.basic.b);
if(values.contains(lit("ConfigSettings")))
{
QVariantMap &settings = m_Legacy->_ConfigSettings;
settings = values[lit("ConfigSettings")].toMap();
if(!processed)
{
if(settings.contains(lit("shader.debug.searchPaths")))
{
QStringList searchPaths = settings[lit("shader.debug.searchPaths")].toString().split(
QLatin1Char(';'), QString::SkipEmptyParts);
SDObject *debug = RENDERDOC_SetConfigSetting("DXBC.Debug.SearchDirPaths");
debug->DeleteChildren();
debug->data.children.resize(searchPaths.size());
for(int i = 0; i < searchPaths.size(); i++)
debug->data.children[i] = makeSDString("$el", 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;
}
// mark the settings as ported so we don't do it again
settings[lit("modern.config.ported")] = true;
}
if(saveConfig)
RENDERDOC_SaveConfigSettings();
}
static QMutex RemoteHostLock;
@@ -265,20 +363,6 @@ void PersistantConfig::RemoveRemoteHost(RemoteHost host)
void PersistantConfig::UpdateEnumeratedProtocolDevices()
{
QString androidSDKPath = (!Android_SDKPath.isEmpty() && QFile::exists(Android_SDKPath))
? QString(Android_SDKPath)
: QString();
SetConfigSetting("androidSDKPath", androidSDKPath);
QString androidJDKPath = (!Android_JDKPath.isEmpty() && QFile::exists(Android_JDKPath))
? QString(Android_JDKPath)
: QString();
SetConfigSetting("androidJDKPath", androidJDKPath);
SetConfigSetting("MaxConnectTimeout", QString::number(Android_MaxConnectTimeout));
rdcarray<RemoteHost> enumeratedDevices;
rdcarray<rdcstr> protocols;
@@ -350,24 +434,20 @@ bool PersistantConfig::SetStyle()
return false;
}
PersistantConfig::PersistantConfig()
{
m_Legacy = new LegacyData;
}
PersistantConfig::~PersistantConfig()
{
delete m_Legacy;
}
bool PersistantConfig::Load(const rdcstr &filename)
{
bool ret = Deserialize(filename);
// perform some sanitisation to make sure config is always in sensible state
for(const rdcstrpair &key : ConfigSettings)
{
// redundantly set each setting so it is flushed to the core dll
SetConfigSetting(key.first, key.second);
}
RENDERDOC_SetConfigSetting("Disassembly_FriendlyNaming", ShaderViewer_FriendlyNaming ? "1" : "0");
RENDERDOC_SetConfigSetting("ExternalTool_RGPIntegration", ExternalTool_RGPIntegration ? "1" : "0");
RDDialog::DefaultBrowsePath = LastFileBrowsePath;
// localhost should always be available as a remote host
@@ -501,9 +581,6 @@ bool PersistantConfig::Save()
if(m_Filename.isEmpty())
return true;
RENDERDOC_SetConfigSetting("Disassembly_FriendlyNaming", ShaderViewer_FriendlyNaming ? "1" : "0");
RENDERDOC_SetConfigSetting("ExternalTool_RGPIntegration", ExternalTool_RGPIntegration ? "1" : "0");
LastFileBrowsePath = RDDialog::DefaultBrowsePath;
// truncate the lists to a maximum of 9 items, allow more to exist in memory
@@ -557,36 +634,6 @@ void AddRecentFile(rdcarray<rdcstr> &recentList, const rdcstr &file)
recentList.push_back(path);
}
void PersistantConfig::SetConfigSetting(const rdcstr &name, const rdcstr &value)
{
if(name.isEmpty())
return;
bool found = false;
for(rdcstrpair &kv : ConfigSettings)
{
if(kv.first == name)
{
kv.second = value;
found = true;
break;
}
}
if(!found)
ConfigSettings.push_back(make_rdcpair(name, value));
RENDERDOC_SetConfigSetting(name.data(), value.data());
}
rdcstr PersistantConfig::GetConfigSetting(const rdcstr &name)
{
for(const rdcstrpair &kv : ConfigSettings)
if(kv.first == name)
return kv.second;
return "";
}
ShaderProcessingTool::ShaderProcessingTool(const QVariant &var)
{
QVariantMap map = var.toMap();
+7 -58
View File
@@ -302,8 +302,6 @@ DECLARE_REFLECTION_STRUCT(BugReport);
\
CONFIG_SETTING_VAL(public, bool, bool, TextureViewer_PerTexYFlip, false) \
\
CONFIG_SETTING_VAL(public, bool, bool, ShaderViewer_FriendlyNaming, true) \
\
CONFIG_SETTING_VAL(public, bool, bool, AlwaysReplayLocally, false) \
\
CONFIG_SETTING_VAL(public, int, int, LocalProxyAPI, -1) \
@@ -336,12 +334,6 @@ DECLARE_REFLECTION_STRUCT(BugReport);
\
CONFIG_SETTING_VAL(public, bool, bool, Font_PreferMonospaced, false) \
\
CONFIG_SETTING_VAL(public, QString, rdcstr, Android_SDKPath, "") \
\
CONFIG_SETTING_VAL(public, QString, rdcstr, Android_JDKPath, "") \
\
CONFIG_SETTING_VAL(public, int, int, Android_MaxConnectTimeout, 30) \
\
CONFIG_SETTING_VAL(public, QDateTime, rdcdatetime, UnsupportedAndroid_LastUpdate, \
rdcdatetime(2015, 01, 01)) \
\
@@ -359,8 +351,6 @@ DECLARE_REFLECTION_STRUCT(BugReport);
CONFIG_SETTING_VAL(public, QDateTime, rdcdatetime, DegradedCapture_LastUpdate, \
rdcdatetime(2015, 01, 01)) \
\
CONFIG_SETTING_VAL(public, bool, bool, ExternalTool_RGPIntegration, false) \
\
CONFIG_SETTING_VAL(public, QString, rdcstr, ExternalTool_RadeonGPUProfiler, "") \
\
CONFIG_SETTING_VAL(public, bool, bool, Tips_HasSeenFirst, false) \
@@ -385,8 +375,6 @@ DECLARE_REFLECTION_STRUCT(BugReport);
\
CONFIG_SETTING(public, QVariantList, rdcarray<rdcstr>, AlwaysLoad_Extensions) \
\
CONFIG_SETTING(private, QVariantMap, rdcstrpairs, ConfigSettings) \
\
CONFIG_SETTING(private, QVariantList, rdcarray<RemoteHost>, RemoteHostList)
DOCUMENT(R"(The unit that GPU durations are displayed in.
@@ -458,6 +446,8 @@ As the name suggests, this is used for tracking a 'recent file' list.
)");
void RemoveRecentFile(rdcarray<rdcstr> &recentList, const rdcstr &file);
struct LegacyData;
DOCUMENT2(R"(A persistant config file that is automatically loaded and saved, which contains any
settings and information that needs to be preserved from one run to the next.
@@ -533,13 +523,6 @@ For more information about some of these settings that are user-facing see
Defaults to ``False``.
.. data:: ShaderViewer_FriendlyNaming
``True`` if the :class:`ShaderViewer` should replace register names with the high-level language
variable names where possible.
Defaults to ``True``.
.. data:: AlwaysReplayLocally
``True`` if when loading a capture that was originally captured on a remote device but uses an
@@ -655,24 +638,6 @@ For more information about some of these settings that are user-facing see
Defaults to ``False``.
.. data:: Android_SDKPath
The path to the root of the android SDK, to locate android tools to use for android remote hosts.
Defaults to using the tools distributed with RenderDoc.
.. data:: Android_JDKPath
The path to the root of the Java JDK, to locate java for running android java tools.
Defaults to using the JAVA_HOME environment variable, if set.
.. data:: Android_MaxConnectTimeout
The maximum timeout in seconds to wait when launching an Android package.
Defaults to ``30``.
.. data:: UnsupportedAndroid_LastUpdate
A date containing the last time that the user was warned about an Android device being older than
@@ -713,10 +678,6 @@ For more information about some of these settings that are user-facing see
A date containing the last time that the user was warned about captures being loaded in degraded
support. This prevents the user being spammed if their hardware is low spec.
.. data:: ExternalTool_RGPIntegration
Whether to enable integration with the external Radeon GPU Profiler tool.
.. data:: ExternalTool_RadeonGPUProfiler
The path to the executable of the external Radeon GPU Profiler tool.
@@ -788,7 +749,6 @@ For more information about some of these settings that are user-facing see
A list of strings with extension packages to always load on startup, without needing manual
enabling.
)");
class PersistantConfig
{
@@ -822,7 +782,7 @@ public:
DOCUMENT("");
CONFIG_SETTINGS()
public:
PersistantConfig() {}
PersistantConfig();
~PersistantConfig();
DOCUMENT(R"(Loads the config from a given filename. This happens automatically on startup, so it's
@@ -854,21 +814,6 @@ loading. It can explicitly save and close before relaunching.
DOCUMENT("Configures the :class:`Formatter` class with the settings from this config.");
void SetupFormatting();
DOCUMENT(R"(Sets an arbitrary dynamic setting similar to a key-value store. This can be used for
storing custom settings to be persisted without needing to modify code.
:param str name: The name of the setting. Any existing setting will be overwritten.
:param str value: The contents of the setting.
)");
void SetConfigSetting(const rdcstr &name, const rdcstr &value);
DOCUMENT(R"(Retrieves an arbitrary dynamic setting. See :meth:`SetConfigSetting`.
:param str name: The name of the setting.
:return: The value of the setting, or the empty string if the setting did not exist.
:rtype: ``str``
)");
rdcstr GetConfigSetting(const rdcstr &name);
DOCUMENT(R"(Sets the UI style to the value in :data:`UIStyle`.
Changing the style after the application has started may not properly update everything, so to be
@@ -891,4 +836,8 @@ private:
#endif
rdcstr m_Filename;
// legacy storage for config settings that were ported into the core config. We keep them here
// so we can store them out again, to keep the config the same for a while even if it's unused
LegacyData *m_Legacy;
};
@@ -107,6 +107,10 @@ bool PersistantConfig::SetStyle()
return false;
}
PersistantConfig::PersistantConfig()
{
}
PersistantConfig::~PersistantConfig()
{
}
@@ -159,15 +163,6 @@ void RemoveRecentFile(rdcarray<rdcstr> &recentList, const rdcstr &file)
{
}
void PersistantConfig::SetConfigSetting(const rdcstr &name, const rdcstr &value)
{
}
rdcstr PersistantConfig::GetConfigSetting(const rdcstr &name)
{
return "";
}
////////////////////////////////////////////////////////////////////////////////
// RemoteHost.cpp stubs
////////////////////////////////////////////////////////////////////////////////
+701
View File
@@ -0,0 +1,701 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "ConfigEditor.h"
#include <float.h>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include "Code/QRDUtils.h"
#include "Code/Resources.h"
#include "Widgets/Extended/RDHeaderView.h"
#include "Widgets/Extended/RDLineEdit.h"
#include "Widgets/OrderedListEditor.h"
#include "ui_ConfigEditor.h"
static QString valueString(const SDObject *o)
{
if(o->type.basetype == SDBasic::String)
return o->data.str;
if(o->type.basetype == SDBasic::UnsignedInteger)
return Formatter::Format(o->data.basic.u);
if(o->type.basetype == SDBasic::SignedInteger)
return Formatter::Format(o->data.basic.i);
if(o->type.basetype == SDBasic::Float)
return Formatter::Format(o->data.basic.d);
if(o->type.basetype == SDBasic::Boolean)
return o->data.basic.b ? lit("True") : lit("False");
if(o->type.basetype == SDBasic::Array)
return lit("{...}");
return lit("??");
}
static bool anyChildChanged(const SDObject *o)
{
SDObject *def = o->FindChild("default");
SDObject *val = o->FindChild("value");
if(val && def)
return !val->HasEqualValue(def);
for(const SDObject *c : o->data.children)
{
if(anyChildChanged(c))
return true;
}
return false;
}
class SettingModel : public QAbstractItemModel
{
public:
SettingModel(ConfigEditor *view) : QAbstractItemModel(view), m_Viewer(view)
{
populateParents(m_Viewer->m_Config, QModelIndex());
}
void refresh()
{
emit beginResetModel();
emit endResetModel();
}
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
SDObject *o = obj(parent);
if(row < 0 || row > rowCount(parent))
return QModelIndex();
return createIndex(row, column, o->data.children[row]);
}
QModelIndex parent(const QModelIndex &index) const override
{
SDObject *o = obj(index);
if(o == m_Viewer->m_Config)
return QModelIndex();
QModelIndex ret = parents[o];
if(!ret.isValid())
return ret;
return createIndex(ret.row(), index.column(), ret.internalPointer());
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
SDObject *o = obj(parent);
// values don't have children
if(o->FindChild("value"))
return 0;
return o->data.children.count();
}
enum Columns
{
Column_Name,
Column_Value,
Column_ResetButton,
Column_Count,
};
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if(!index.isValid())
return 0;
Qt::ItemFlags ret = QAbstractItemModel::flags(index);
if(index.column() == Column_Value)
{
SDObject *o = obj(index);
SDObject *value = o->FindChild("value");
if(value)
{
ret |= Qt::ItemIsEditable;
if(value->type.basetype == SDBasic::Boolean)
ret |= Qt::ItemIsUserCheckable;
}
}
return ret;
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override { return Column_Count; }
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch(section)
{
case Column_Name: return lit("Setting");
case Column_Value: return lit("Value");
case Column_ResetButton: return lit("Reset");
default: break;
}
}
return QVariant();
}
bool setData(const QModelIndex &index, const QVariant &val, int role) override
{
SDObject *o = NULL;
if(role == Qt::UserRole)
{
// if we have setData for user role, that means we got reset. Just need to emit data changed
o = obj(index);
}
else if(index.column() == Column_Value)
{
o = obj(index);
SDObject *value = o->FindChild("value");
if(role == Qt::CheckStateRole && value)
{
value->data.basic.b = (val.toInt() == Qt::CheckState::Checked);
}
else
{
// didn't change anything we care about
o = NULL;
}
}
if(o)
{
// dataChanged this index and all parents (in case a section became non-customised, or
// customised, and it wasn't before)
QModelIndex idx = index;
while(idx.isValid())
{
o = obj(idx);
emit dataChanged(createIndex(idx.row(), 0, o), createIndex(idx.row(), Column_Count, o),
{Qt::DisplayRole, Qt::CheckStateRole, Qt::FontRole});
idx = parents[o];
}
return true;
}
return false;
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if(index.isValid())
{
SDObject *o = obj(index);
int col = index.column();
SDObject *value = o->FindChild("value");
if(role == Qt::UserRole)
{
return QVariant::fromValue((quintptr)o);
}
else if(role == Qt::DisplayRole)
{
switch(col)
{
case Column_Name: return o->name;
case Column_Value:
return value && value->type.basetype != SDBasic::Boolean ? valueString(value)
: QVariant();
case Column_ResetButton: return anyChildChanged(o) ? lit("...") : QVariant();
default: break;
}
}
else if(role == Qt::CheckStateRole && col == Column_Value)
{
if(value && value->type.basetype == SDBasic::Boolean)
return value->data.basic.b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked;
return QVariant();
}
else if(role == Qt::TextAlignmentRole && col == Column_ResetButton)
{
return Qt::AlignHCenter + Qt::AlignTop;
}
else if(role == Qt::ToolTipRole)
{
SDObject *desc = o->FindChild("description");
if(desc)
return desc->AsString();
}
else if(role == Qt::FontRole)
{
if(anyChildChanged(o))
{
QFont font;
font.setBold(true);
return font;
}
}
}
return QVariant();
}
private:
SDObject *obj(const QModelIndex &parent) const
{
SDObject *ret = (SDObject *)parent.internalPointer();
if(ret == NULL)
ret = m_Viewer->m_Config;
return ret;
}
// Qt models need child->parent relationships. We don't have that with SDObject but they are
// immutable so we can cache them
QMap<SDObject *, QModelIndex> parents;
void populateParents(SDObject *o, QModelIndex parent)
{
if(o->FindChild("value"))
return;
int i = 0;
for(SDObject *c : o->GetChildren())
{
parents[c] = parent;
populateParents(c, index(i++, 0, parent));
}
}
ConfigEditor *m_Viewer;
};
class SettingFilterModel : public QSortFilterProxyModel
{
public:
explicit SettingFilterModel(ConfigEditor *view) : QSortFilterProxyModel(view), m_Viewer(view) {}
void setFilter(QString text)
{
m_Text = text;
m_KeyText = m_Text;
m_KeyText.replace(QLatin1Char('.'), QLatin1Char('_'));
emit beginResetModel();
emit endResetModel();
}
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
{
if(m_Text.isEmpty())
return true;
SDObject *o = obj(source_parent);
return matchesAnyChild(o->data.children[source_row]);
}
bool matchesAnyChild(SDObject *o) const
{
if(QString(o->name).contains(m_Text, Qt::CaseInsensitive))
return true;
if(o->FindChild("value"))
{
if(QString(o->FindChild("key")->AsString()).contains(m_KeyText, Qt::CaseInsensitive))
return true;
return false;
}
for(SDObject *c : o->GetChildren())
if(matchesAnyChild(c))
return true;
return false;
}
private:
SDObject *obj(const QModelIndex &parent) const
{
SDObject *ret = (SDObject *)parent.internalPointer();
if(ret == NULL)
ret = m_Viewer->m_Config;
return ret;
}
ConfigEditor *m_Viewer;
QString m_Text;
QString m_KeyText;
};
SettingDelegate::SettingDelegate(ConfigEditor *editor, RDTreeView *parent)
: QStyledItemDelegate(parent), m_Editor(editor), m_View(parent)
{
}
SettingDelegate::~SettingDelegate()
{
}
void SettingDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if(index.column() == SettingModel::Column_ResetButton)
{
SDObject *o = (SDObject *)index.data(Qt::UserRole).toULongLong();
SDObject *def = o->FindChild("default");
SDObject *val = o->FindChild("value");
if(val && def && !val->HasEqualValue(def))
{
// draw the item without text, so we get the proper background/selection/etc.
// we'd like to be able to use the parent delegate's paint here, but either it calls to
// QStyledItemDelegate which will re-fetch the text (bleh), or it calls to the manual
// delegate which could do anything. So for this case we just use the style and skip the
// delegate and hope it works out.
QStyleOptionViewItem opt = option;
QStyledItemDelegate::initStyleOption(&opt, index);
opt.text.clear();
m_Editor->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, m_Editor);
QStyleOptionToolButton buttonOpt;
int size = m_Editor->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, m_Editor);
buttonOpt.iconSize = QSize(size, size);
buttonOpt.subControls = 0;
buttonOpt.activeSubControls = 0;
buttonOpt.features = QStyleOptionToolButton::None;
buttonOpt.arrowType = Qt::NoArrow;
buttonOpt.state = QStyle::State_Active | QStyle::State_Enabled | QStyle::State_AutoRaise;
buttonOpt.rect = option.rect.adjusted(0, 0, -1, -1);
buttonOpt.icon = Icons::arrow_undo();
if(m_View->currentHoverIndex() == index)
buttonOpt.state |= QStyle::State_MouseOver;
m_Editor->style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, painter, m_Editor);
return;
}
}
return QStyledItemDelegate::paint(painter, option, index);
}
QSize SettingDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QStyledItemDelegate::sizeHint(option, index);
}
bool SettingDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index)
{
if(event->type() == QEvent::MouseButtonRelease && index.column() == SettingModel::Column_ResetButton)
{
SDObject *o = (SDObject *)index.data(Qt::UserRole).toULongLong();
SDObject *def = o->FindChild("default");
SDObject *val = o->FindChild("value");
if(def && val)
{
val->data.str = def->data.str;
memcpy(&val->data.basic, &def->data.basic, sizeof(val->data.basic));
val->DeleteChildren();
for(size_t c = 0; c < def->data.children.size(); c++)
val->data.children.push_back(def->data.children[c]->Duplicate());
// call setData() to emit the dataChanged for this element and all parents
model->setData(index, QVariant(), Qt::UserRole);
return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
QWidget *SettingDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QWidget *ret = NULL;
SDObject *o = (SDObject *)index.data(Qt::UserRole).toULongLong();
SDObject *val = o->FindChild("value");
if(val)
{
// bools should have checkboxes
if(val->type.basetype == SDBasic::Boolean)
{
qWarning() << "Unexpected createEditor for boolean " << QString(o->name);
return ret;
}
// for numbers, provide a spinbox
if(val->type.basetype == SDBasic::UnsignedInteger || val->type.basetype == SDBasic::SignedInteger)
{
QSpinBox *spin = new QSpinBox(parent);
ret = spin;
if(val->type.basetype == SDBasic::UnsignedInteger)
spin->setMinimum(0);
else
spin->setMinimum(INT_MIN);
spin->setMaximum(INT_MAX);
}
else if(val->type.basetype == SDBasic::Float)
{
QDoubleSpinBox *spin = new QDoubleSpinBox(parent);
ret = spin;
spin->setSingleStep(0.1);
spin->setMinimum(-FLT_MAX);
spin->setMaximum(FLT_MAX);
}
else if(val->type.basetype == SDBasic::String)
{
if(QString(o->name).contains(lit("DirPath"), Qt::CaseSensitive))
{
QString dir = RDDialog::getExistingDirectory(
m_Editor, tr("Browse for %1").arg(o->FindChild("key")->AsString()));
if(!dir.isEmpty())
{
val->data.str = dir;
// we've handled the edit synchronously, don't create an edit widget
ret = NULL;
// call setData() to emit the dataChanged for this element and all parents
m_View->model()->setData(index, QVariant(), Qt::UserRole);
}
}
else if(QString(o->name).contains(lit("Path"), Qt::CaseSensitive))
{
QString file = RDDialog::getOpenFileName(
m_Editor, tr("Browse for %1").arg(o->FindChild("key")->AsString()));
if(!file.isEmpty())
{
val->data.str = file;
// we've handled the edit synchronously, don't create an edit widget
ret = NULL;
// call setData() to emit the dataChanged for this element and all parents
m_View->model()->setData(index, QVariant(), Qt::UserRole);
}
}
else
{
RDLineEdit *line = new RDLineEdit(parent);
ret = line;
QObject::connect(line, &RDLineEdit::keyPress, this, &SettingDelegate::editorKeyPress);
}
}
else if(val->type.basetype == SDBasic::Array)
{
// only support arrays of strings. Pop up a separate editor to handle this
QDialog listEditor;
listEditor.setWindowTitle(tr("Edit values of %1").arg(QString(o->FindChild("key")->AsString())));
listEditor.setWindowFlags(listEditor.windowFlags() & ~Qt::WindowContextHelpButtonHint);
BrowseMode mode = BrowseMode::None;
if(QString(o->name).contains(lit("DirPath"), Qt::CaseSensitive))
mode = BrowseMode::Folder;
else if(QString(o->name).contains(lit("Path"), Qt::CaseSensitive))
mode = BrowseMode::File;
OrderedListEditor list(tr("Entry"), mode);
QVBoxLayout layout;
QDialogButtonBox okCancel;
okCancel.setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
layout.addWidget(&list);
layout.addWidget(&okCancel);
QObject::connect(&okCancel, &QDialogButtonBox::accepted, &listEditor, &QDialog::accept);
QObject::connect(&okCancel, &QDialogButtonBox::rejected, &listEditor, &QDialog::reject);
listEditor.setLayout(&layout);
QStringList items;
for(SDObject *c : val->data.children)
items << c->data.str;
list.setItems(items);
int res = RDDialog::show(&listEditor);
if(res)
{
items = list.getItems();
val->DeleteChildren();
val->data.children.resize(items.size());
for(int i = 0; i < items.size(); i++)
val->data.children[i] = makeSDString("$el", items[i]);
}
// we've handled the edit synchronously, don't create an edit widget
ret = NULL;
// call setData() to emit the dataChanged for this element and all parents
m_View->model()->setData(index, QVariant(), Qt::UserRole);
}
else
{
qWarning() << "Unexpected type of " << QString(o->name)
<< " to edit: " << ToQStr(val->type.basetype);
}
}
return ret;
}
void SettingDelegate::editorKeyPress(QKeyEvent *ev)
{
QLineEdit *line = qobject_cast<QLineEdit *>(QObject::sender());
if(ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter)
{
commitData(line);
closeEditor(line);
}
else if(ev->key() == Qt::Key_Escape)
{
closeEditor(line);
}
}
void SettingDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
SDObject *o = (SDObject *)index.data(Qt::UserRole).toULongLong();
SDObject *val = o->FindChild("value");
if(val)
{
if(val->type.basetype == SDBasic::Boolean)
{
qWarning() << "Unexpected setEditorData for boolean " << QString(o->name);
return;
}
if(val->type.basetype == SDBasic::UnsignedInteger)
qobject_cast<QSpinBox *>(editor)->setValue(val->AsUInt32() & 0x7fffffffU);
else if(val->type.basetype == SDBasic::SignedInteger)
qobject_cast<QSpinBox *>(editor)->setValue(val->AsInt32());
else if(val->type.basetype == SDBasic::Float)
qobject_cast<QDoubleSpinBox *>(editor)->setValue(val->AsDouble());
else if(val->type.basetype == SDBasic::String)
qobject_cast<QLineEdit *>(editor)->setText(val->AsString());
else
qWarning() << "Unexpected type of " << QString(o->name) << ": " << ToQStr(val->type.basetype);
}
}
void SettingDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
SDObject *o = (SDObject *)index.data(Qt::UserRole).toULongLong();
SDObject *val = o->FindChild("value");
if(val)
{
if(val->type.basetype == SDBasic::Boolean)
{
qWarning() << "Unexpected setModelData for boolean " << QString(o->name);
return;
}
if(val->type.basetype == SDBasic::UnsignedInteger)
val->data.basic.u = qMax(0, qobject_cast<QSpinBox *>(editor)->value());
else if(val->type.basetype == SDBasic::SignedInteger)
val->data.basic.i = qobject_cast<QSpinBox *>(editor)->value();
else if(val->type.basetype == SDBasic::Float)
val->data.basic.d = qobject_cast<QSpinBox *>(editor)->value();
else if(val->type.basetype == SDBasic::String)
val->data.str = qobject_cast<QLineEdit *>(editor)->text();
else
qWarning() << "Unexpected type of " << QString(o->name) << ": " << ToQStr(val->type.basetype);
}
}
ConfigEditor::ConfigEditor(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigEditor)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_Config = RENDERDOC_SetConfigSetting("");
m_SettingModel = new SettingModel(this);
m_FilterModel = new SettingFilterModel(this);
m_FilterModel->setSourceModel(m_SettingModel);
ui->settings->setModel(m_FilterModel);
{
RDHeaderView *header = new RDHeaderView(Qt::Horizontal, ui->settings);
ui->settings->setHeader(header);
header->setColumnStretchHints({-1, 1, -1});
}
ui->settings->setItemDelegate(new SettingDelegate(this, ui->settings));
}
ConfigEditor::~ConfigEditor()
{
delete ui;
}
void ConfigEditor::on_filter_textChanged(const QString &text)
{
RDTreeViewExpansionState state;
ui->settings->saveExpansion(state, 0);
m_FilterModel->setFilter(text);
ui->settings->applyExpansion(state, 0);
}
void ConfigEditor::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
return;
QDialog::keyPressEvent(e);
}
+99
View File
@@ -0,0 +1,99 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <QDialog>
#include <QString>
#include <QStyledItemDelegate>
#include <QVector>
struct SDObject;
namespace Ui
{
class ConfigEditor;
}
class ConfigEditor;
class SettingModel;
class SettingFilterModel;
class RDTreeView;
class SettingDelegate : public QStyledItemDelegate
{
Q_OBJECT
ConfigEditor *m_Editor;
RDTreeView *m_View;
public:
explicit SettingDelegate(ConfigEditor *editor, RDTreeView *parent);
~SettingDelegate();
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index) override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
public slots:
void editorKeyPress(QKeyEvent *ev);
};
class ConfigEditor : public QDialog
{
Q_OBJECT
public:
explicit ConfigEditor(QWidget *parent = 0);
~ConfigEditor();
private slots:
// automatic slots
void on_filter_textChanged(const QString &text);
private:
void keyPressEvent(QKeyEvent *e) override;
SettingModel *m_SettingModel = NULL;
SettingFilterModel *m_FilterModel = NULL;
SDObject *m_Config = NULL;
friend class SettingModel;
friend class SettingFilterModel;
friend class SettingDelegate;
Ui::ConfigEditor *ui;
};
+136
View File
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigEditor</class>
<widget class="QDialog" name="ConfigEditor">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Advanced Config Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="warning">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>106</red>
<green>104</green>
<blue>100</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>Editing any of these settings could cause crashes or unpredictable behaviour. Be sure you know what you are doing before editing any of them!</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="filter">
<property name="placeholderText">
<string>Filter the settings</string>
</property>
</widget>
</item>
<item>
<widget class="RDTreeView" name="settings"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttons">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RDTreeView</class>
<extends>QTreeView</extends>
<header>Widgets/Extended/RDTreeView.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttons</sender>
<signal>accepted()</signal>
<receiver>ConfigEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>379</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>200</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttons</sender>
<signal>rejected()</signal>
<receiver>ConfigEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>379</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>200</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+71 -38
View File
@@ -34,6 +34,7 @@
#include "Widgets/OrderedListEditor.h"
#include "Widgets/ReplayOptionsSelector.h"
#include "CaptureDialog.h"
#include "ConfigEditor.h"
#include "ui_SettingsDialog.h"
SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent)
@@ -121,17 +122,19 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent)
ui->deleteShaderTool->setEnabled(false);
ui->editShaderTool->setEnabled(false);
ui->ExternalTool_RGPIntegration->setChecked(m_Ctx.Config().ExternalTool_RGPIntegration);
ui->ExternalTool_RGPIntegration->setChecked(RENDERDOC_GetConfigSetting("AMD.RGP.Enable")->AsBool());
ui->ExternalTool_RadeonGPUProfiler->setText(m_Ctx.Config().ExternalTool_RadeonGPUProfiler);
ui->Android_SDKPath->setText(m_Ctx.Config().Android_SDKPath);
ui->Android_JDKPath->setText(m_Ctx.Config().Android_JDKPath);
ui->Android_MaxConnectTimeout->setValue(m_Ctx.Config().Android_MaxConnectTimeout);
ui->Android_SDKPath->setText(RENDERDOC_GetConfigSetting("Android.SDKDirPath")->AsString());
ui->Android_JDKPath->setText(RENDERDOC_GetConfigSetting("Android.JDKDirPath")->AsString());
ui->Android_MaxConnectTimeout->setValue(
RENDERDOC_GetConfigSetting("Android.MaxConnectTimeout")->AsUInt32());
ui->TextureViewer_ResetRange->setChecked(m_Ctx.Config().TextureViewer_ResetRange);
ui->TextureViewer_PerTexSettings->setChecked(m_Ctx.Config().TextureViewer_PerTexSettings);
ui->TextureViewer_PerTexYFlip->setChecked(m_Ctx.Config().TextureViewer_PerTexYFlip);
ui->ShaderViewer_FriendlyNaming->setChecked(m_Ctx.Config().ShaderViewer_FriendlyNaming);
ui->ShaderViewer_FriendlyNaming->setChecked(
RENDERDOC_GetConfigSetting("DXBC.Disassembly.FriendlyNaming")->AsBool());
ui->CheckUpdate_AllowChecks->setChecked(m_Ctx.Config().CheckUpdate_AllowChecks);
ui->Font_PreferMonospaced->setChecked(m_Ctx.Config().Font_PreferMonospaced);
@@ -411,6 +414,15 @@ void SettingsDialog::on_analyticsDescribeLabel_linkActivated(const QString &link
}
// core
void SettingsDialog::on_configEditor_clicked()
{
ConfigEditor editor;
RDDialog::show(&editor);
RENDERDOC_SaveConfigSettings();
}
void SettingsDialog::on_chooseSearchPaths_clicked()
{
QDialog listEditor;
@@ -431,22 +443,38 @@ void SettingsDialog::on_chooseSearchPaths_clicked()
listEditor.setLayout(&layout);
QString setting = m_Ctx.Config().GetConfigSetting("shader.debug.searchPaths");
const SDObject *getPaths = RENDERDOC_GetConfigSetting("DXBC.Debug.SearchDirPaths");
list.setItems(setting.split(QLatin1Char(';'), QString::SkipEmptyParts));
QStringList items;
for(SDObject *c : getPaths->data.children)
items << c->data.str;
list.setItems(items);
int res = RDDialog::show(&listEditor);
if(res)
m_Ctx.Config().SetConfigSetting(lit("shader.debug.searchPaths"),
list.getItems().join(QLatin1Char(';')));
{
items = list.getItems();
SDObject *setPaths = RENDERDOC_SetConfigSetting("DXBC.Debug.SearchDirPaths");
setPaths->DeleteChildren();
setPaths->data.children.resize(items.size());
for(int i = 0; i < items.size(); i++)
setPaths->data.children[i] = makeSDString("$el", items[i]);
RENDERDOC_SaveConfigSettings();
}
}
void SettingsDialog::on_ExternalTool_RGPIntegration_toggled(bool checked)
{
m_Ctx.Config().ExternalTool_RGPIntegration = checked;
RENDERDOC_SetConfigSetting("AMD.RGP.Enable")->data.basic.b = checked;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
void SettingsDialog::on_ExternalTool_RadeonGPUProfiler_textEdited(const QString &rgp)
@@ -499,9 +527,9 @@ void SettingsDialog::on_TextureViewer_ResetRange_toggled(bool checked)
// shader viewer
void SettingsDialog::on_ShaderViewer_FriendlyNaming_toggled(bool checked)
{
m_Ctx.Config().ShaderViewer_FriendlyNaming = ui->ShaderViewer_FriendlyNaming->isChecked();
RENDERDOC_SetConfigSetting("DXBC.Disassembly.FriendlyNaming")->data.basic.b = checked;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
void SettingsDialog::addProcessor(const ShaderProcessingTool &tool)
@@ -926,55 +954,60 @@ void SettingsDialog::on_browseTempCaptureDirectory_clicked()
void SettingsDialog::on_browseAndroidSDKPath_clicked()
{
QString adb = RDDialog::getExistingDirectory(
QString sdk = RDDialog::getExistingDirectory(
this, tr("Locate SDK root folder (containing build-tools, platform-tools)"),
QFileInfo(m_Ctx.Config().Android_SDKPath).absoluteDir().path());
QFileInfo(RENDERDOC_GetConfigSetting("Android.SDKDirPath")->AsString()).absoluteDir().path());
if(!adb.isEmpty())
if(!sdk.isEmpty())
{
ui->Android_SDKPath->setText(adb);
m_Ctx.Config().Android_SDKPath = adb;
}
ui->Android_SDKPath->setText(sdk);
RENDERDOC_SetConfigSetting("Android.SDKDirPath")->data.str = sdk;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
}
void SettingsDialog::on_Android_SDKPath_textEdited(const QString &adb)
void SettingsDialog::on_Android_SDKPath_textEdited(const QString &sdk)
{
if(QFileInfo::exists(adb) || adb.isEmpty())
m_Ctx.Config().Android_SDKPath = adb;
if(QFileInfo::exists(sdk) || sdk.isEmpty())
{
RENDERDOC_SetConfigSetting("Android.SDKDirPath")->data.str = sdk;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
}
void SettingsDialog::on_browseJDKPath_clicked()
{
QString adb =
RDDialog::getExistingDirectory(this, tr("Locate JDK root folder (containing bin, jre, lib)"),
QFileInfo(m_Ctx.Config().Android_JDKPath).absoluteDir().path());
QString jdk = RDDialog::getExistingDirectory(
this, tr("Locate JDK root folder (containing bin, jre, lib)"),
QFileInfo(RENDERDOC_GetConfigSetting("Android.JDKDirPath")->AsString()).absoluteDir().path());
if(!adb.isEmpty())
if(!jdk.isEmpty())
{
ui->Android_JDKPath->setText(adb);
m_Ctx.Config().Android_JDKPath = adb;
}
ui->Android_JDKPath->setText(jdk);
RENDERDOC_SetConfigSetting("Android.JDKDirPath")->data.str = jdk;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
}
void SettingsDialog::on_Android_JDKPath_textEdited(const QString &adb)
void SettingsDialog::on_Android_JDKPath_textEdited(const QString &jdk)
{
if(QFileInfo::exists(adb) || adb.isEmpty())
m_Ctx.Config().Android_JDKPath = adb;
if(QFileInfo::exists(jdk) || jdk.isEmpty())
{
RENDERDOC_SetConfigSetting("Android.JDKDirPath")->data.str = jdk;
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
}
void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout)
{
m_Ctx.Config().Android_MaxConnectTimeout = ui->Android_MaxConnectTimeout->value();
RENDERDOC_SetConfigSetting("Android.MaxConnectTimeout")->data.basic.u =
(uint32_t)ui->Android_MaxConnectTimeout->value();
m_Ctx.Config().Save();
RENDERDOC_SaveConfigSettings();
}
void SettingsDialog::on_UIStyle_currentIndexChanged(int index)
+3 -2
View File
@@ -71,6 +71,7 @@ private slots:
void on_analyticsOptOut_toggled(bool checked);
// core
void on_configEditor_clicked();
void on_chooseSearchPaths_clicked();
void on_ExternalTool_RGPIntegration_toggled(bool checked);
void on_ExternalTool_RadeonGPUProfiler_textEdited(const QString &rgp);
@@ -108,8 +109,8 @@ private slots:
void on_browseAndroidSDKPath_clicked();
void on_browseJDKPath_clicked();
void on_Android_MaxConnectTimeout_valueChanged(double timeout);
void on_Android_SDKPath_textEdited(const QString &path);
void on_Android_JDKPath_textEdited(const QString &path);
void on_Android_SDKPath_textEdited(const QString &sdk);
void on_Android_JDKPath_textEdited(const QString &jdk);
// manual slots
void formatter_valueChanged(int value);
+55 -41
View File
@@ -482,27 +482,7 @@ e.g. 1000 * 10 = 1e4</string>
<string>Core</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Shader debug search paths</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="chooseSearchPaths">
<property name="text">
<string>Choose paths</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_17">
<property name="toolTip">
<string>Locates the RadeonGPUProfiler.exe which will be used to interop with when generating and opening RGP profiles.</string>
@@ -512,24 +492,14 @@ e.g. 1000 * 10 = 1e4</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLineEdit" name="ExternalTool_RadeonGPUProfiler">
<property name="toolTip">
<string>Locates the RadeonGPUProfiler.exe which will be used to interop with when generating and opening RGP profiles.</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="browseRGPPath">
<property name="toolTip">
<string>Locates the RadeonGPUProfiler.exe which will be used to interop with when generating and opening RGP profiles.</string>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="5" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -542,7 +512,49 @@ e.g. 1000 * 10 = 1e4</string>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="browseRGPPath">
<property name="toolTip">
<string>Locates the RadeonGPUProfiler.exe which will be used to interop with when generating and opening RGP profiles.</string>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_18">
<property name="toolTip">
<string>RenderDoc can optionally have integration with AMD's Radeon GPU Profiler, to allow capturing RGP from RenderDoc and allowing interop between the two.
After interop is enabled you will need to reload any capture.</string>
</property>
<property name="text">
<string>Enable Radeon GPU Profiler integration (requires capture reload)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Shader debug search paths</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="chooseSearchPaths">
<property name="text">
<string>Choose paths</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="ExternalTool_RGPIntegration">
<property name="toolTip">
<string>RenderDoc can optionally have integration with AMD's Radeon GPU Profiler, to allow capturing RGP from RenderDoc and allowing interop between the two.
@@ -554,15 +566,17 @@ After interop is enabled you will need to reload any capture.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<property name="toolTip">
<string>RenderDoc can optionally have integration with AMD's Radeon GPU Profiler, to allow capturing RGP from RenderDoc and allowing interop between the two.
After interop is enabled you will need to reload any capture.</string>
</property>
<item row="0" column="1">
<widget class="QPushButton" name="configEditor">
<property name="text">
<string>Enable Radeon GPU Profiler integration (requires capture reload)</string>
<string>Config Editor</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Open Advanced Config Editor</string>
</property>
</widget>
</item>
+3
View File
@@ -237,6 +237,7 @@ SOURCES += Code/qrenderdoc.cpp \
Widgets/FindReplace.cpp \
Widgets/Extended/RDSplitter.cpp \
Windows/Dialogs/TipsDialog.cpp \
Windows/Dialogs/ConfigEditor.cpp \
Windows/PythonShell.cpp \
Windows/Dialogs/PerformanceCounterSelection.cpp \
Windows/PerformanceCounterViewer.cpp \
@@ -315,6 +316,7 @@ HEADERS += Code/CaptureContext.h \
Widgets/FindReplace.h \
Widgets/Extended/RDSplitter.h \
Windows/Dialogs/TipsDialog.h \
Windows/Dialogs/ConfigEditor.h \
Windows/PythonShell.h \
Windows/Dialogs/PerformanceCounterSelection.h \
Windows/PerformanceCounterViewer.h \
@@ -356,6 +358,7 @@ FORMS += Windows/Dialogs/AboutDialog.ui \
Windows/Dialogs/EnvironmentEditor.ui \
Widgets/FindReplace.ui \
Windows/Dialogs/TipsDialog.ui \
Windows/Dialogs/ConfigEditor.ui \
Windows/PythonShell.ui \
Windows/Dialogs/PerformanceCounterSelection.ui \
Windows/PerformanceCounterViewer.ui \
+15
View File
@@ -663,6 +663,7 @@
<ClCompile Include="$(IntDir)generated\moc_TextureSaveDialog.cpp" />
<ClCompile Include="$(IntDir)generated\moc_TextureViewer.cpp" />
<ClCompile Include="$(IntDir)generated\moc_TipsDialog.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ConfigEditor.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ThumbnailStrip.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ReplayOptionsSelector.cpp" />
<ClCompile Include="$(IntDir)generated\moc_ToolWindowManager.cpp" />
@@ -738,6 +739,7 @@
<ClCompile Include="Windows\Dialogs\SuggestRemoteDialog.cpp" />
<ClCompile Include="Windows\Dialogs\TextureSaveDialog.cpp" />
<ClCompile Include="Windows\Dialogs\TipsDialog.cpp" />
<ClCompile Include="Windows\Dialogs\ConfigEditor.cpp" />
<ClCompile Include="Windows\Dialogs\VirtualFileDialog.cpp" />
<ClCompile Include="Windows\EventBrowser.cpp" />
<ClCompile Include="3rdparty\flowlayout\FlowLayout.cpp">
@@ -957,6 +959,7 @@
<ClInclude Include="$(IntDir)generated\ui_SuggestRemoteDialog.h" />
<ClInclude Include="$(IntDir)generated\ui_TextureSaveDialog.h" />
<ClInclude Include="$(IntDir)generated\ui_TipsDialog.h" />
<ClInclude Include="$(IntDir)generated\ui_ConfigEditor.h" />
<ClInclude Include="$(IntDir)generated\ui_TextureViewer.h" />
<ClInclude Include="$(IntDir)generated\ui_ThumbnailStrip.h" />
<ClInclude Include="$(IntDir)generated\ui_ReplayOptionsSelector.h" />
@@ -1262,6 +1265,12 @@
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\ConfigEditor.h">
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015" -I"$(ProjectDir)3rdparty\qt\include" -I"$(ProjectDir)3rdparty\qt\include\QtWidgets" -I"$(ProjectDir)3rdparty\qt\include\QtGui" -I"$(ProjectDir)3rdparty\qt\include\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
<Message>MOC %(Filename).h</Message>
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\VirtualFileDialog.h">
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015" -I"$(ProjectDir)3rdparty\qt\include" -I"$(ProjectDir)3rdparty\qt\include\QtWidgets" -I"$(ProjectDir)3rdparty\qt\include\QtGui" -I"$(ProjectDir)3rdparty\qt\include\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp"</Command>
@@ -1540,6 +1549,12 @@
<Message>UIC %(Filename).ui</Message>
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\ConfigEditor.ui">
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
<Message>UIC %(Filename).ui</Message>
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\VirtualFileDialog.ui">
<AdditionalInputs>%(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Command>"$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
+18 -1
View File
@@ -729,6 +729,12 @@
<ClCompile Include="Code\BufferFormatter.cpp">
<Filter>Code</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)generated\moc_ConfigEditor.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="Windows\Dialogs\ConfigEditor.cpp">
<Filter>Windows\Dialogs</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="3rdparty\flowlayout\FlowLayout.h">
@@ -1088,6 +1094,9 @@
<ClInclude Include="$(IntDir)generated\ui_ReplayOptionsSelector.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="$(IntDir)generated\ui_ConfigEditor.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Code\pyrenderdoc\pyconversion.h">
@@ -1478,7 +1487,15 @@
<CustomBuild Include="Widgets\ReplayOptionsSelector.h">
<Filter>Widgets</Filter>
</CustomBuild>
<CustomBuild Include="Widgets\ReplayOptionsSelector.ui" />
<CustomBuild Include="Widgets\ReplayOptionsSelector.ui">
<Filter>Widgets</Filter>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\ConfigEditor.h">
<Filter>Windows\Dialogs</Filter>
</CustomBuild>
<CustomBuild Include="Windows\Dialogs\ConfigEditor.ui">
<Filter>Windows\Dialogs</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="Resources\action.png">
+2
View File
@@ -132,6 +132,8 @@ set(sources
core/target_control.cpp
core/remote_server.cpp
core/remote_server.h
core/settings.cpp
core/settings.h
core/replay_proxy.cpp
core/replay_proxy.h
core/intervals.h
+6 -4
View File
@@ -30,10 +30,15 @@
#include "common/threading.h"
#include "core/core.h"
#include "core/remote_server.h"
#include "core/settings.h"
#include "replay/replay_driver.h"
#include "strings/string_utils.h"
#include "android_utils.h"
RDOC_CONFIG(uint32_t, Android_MaxConnectTimeout, 30,
"Maximum time in seconds to try connecting to the target app before giving up. "
"Useful primarily for apps that take a very long time to start up.");
namespace Android
{
void adbForwardPorts(uint16_t portbase, const rdcstr &deviceID, uint16_t jdwpPort, int pid,
@@ -1166,10 +1171,7 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w
ret.status = ReplayStatus::InjectionFailed;
uint32_t elapsed = 0,
timeout =
1000 *
RDCMAX(5, atoi(RenderDoc::Inst().GetConfigSetting("MaxConnectTimeout").c_str()));
uint32_t elapsed = 0, timeout = 1000 * RDCMAX(5U, Android_MaxConnectTimeout);
while(elapsed < timeout)
{
// Check if the target app has started yet and we can connect to it.
+11 -2
View File
@@ -24,9 +24,18 @@
#include "common/formatting.h"
#include "core/core.h"
#include "core/settings.h"
#include "strings/string_utils.h"
#include "android_utils.h"
RDOC_CONFIG(rdcstr, Android_SDKDirPath, "",
"The location of the root of the Android SDK. This path "
"should contain folders such as build-tools and platform-tools.");
RDOC_CONFIG(rdcstr, Android_JDKDirPath, "",
"The location of the root of the Java JDK. This path "
"should contain folders such as bin and lib.");
namespace Android
{
static bool adbKillServer = false;
@@ -178,8 +187,8 @@ rdcstr getToolPath(ToolDir subdir, const rdcstr &toolname, bool checkExist)
// its client-server setup, so if we run our bundled adb that might be newer than the user's, they
// will then get fighting back and forth when trying to run their own.
rdcstr sdk = RenderDoc::Inst().GetConfigSetting("androidSDKPath");
rdcstr jdk = RenderDoc::Inst().GetConfigSetting("androidJDKPath");
rdcstr sdk = Android_SDKDirPath;
rdcstr jdk = Android_JDKDirPath;
ToolPathCache &cache = getCache();
+25 -5
View File
@@ -1987,12 +1987,32 @@ DOCUMENT(R"(Returns the current process's memory usage in bytes
)");
extern "C" RENDERDOC_API uint64_t RENDERDOC_CC RENDERDOC_GetCurrentProcessMemoryUsage();
DOCUMENT("Internal function for retrieving a config setting.");
extern "C" RENDERDOC_API const char *RENDERDOC_CC RENDERDOC_GetConfigSetting(const char *name);
DOCUMENT(R"(Return a read-only handle to the :class:`SDObject` corresponding to a given setting's
value object.
DOCUMENT("Internal function for setting a config setting.");
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetConfigSetting(const char *name,
const char *value);
If an empty string is passed, the root object is returned containing all settings and setting
categories. Categories contain other categories and settings, settings contain children that include
the setting's value, description, etc.
If no such setting exists, `None` is returned.
:return: The specified setting.
:rtype: ``SDObject``
)");
extern "C" RENDERDOC_API const SDObject *RENDERDOC_CC RENDERDOC_GetConfigSetting(const char *name);
DOCUMENT(R"(Return a mutable handle to the :class:`SDObject` corresponding to a given setting's
value object.
If no such setting exists, `None` is returned.
:return: The specified setting.
:rtype: ``SDObject``
)");
extern "C" RENDERDOC_API SDObject *RENDERDOC_CC RENDERDOC_SetConfigSetting(const char *name);
DOCUMENT("Internal function for saving config settings.");
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SaveConfigSettings();
DOCUMENT("Internal function for setting UI theme colors.");
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetColors(FloatVector darkChecker,
+21
View File
@@ -22,6 +22,27 @@
* THE SOFTWARE.
******************************************************************************/
template <>
rdcstr DoStringise(const SDBasic &el)
{
BEGIN_ENUM_STRINGISE(SDBasic);
{
STRINGISE_ENUM_CLASS(Chunk);
STRINGISE_ENUM_CLASS(Struct);
STRINGISE_ENUM_CLASS(Array);
STRINGISE_ENUM_CLASS(Null);
STRINGISE_ENUM_CLASS(Buffer);
STRINGISE_ENUM_CLASS(String);
STRINGISE_ENUM_CLASS(Enum);
STRINGISE_ENUM_CLASS(UnsignedInteger);
STRINGISE_ENUM_CLASS(SignedInteger);
STRINGISE_ENUM_CLASS(Float);
STRINGISE_ENUM_CLASS(Boolean);
STRINGISE_ENUM_CLASS(Character);
}
END_ENUM_STRINGISE();
}
template <>
rdcstr DoStringise(const ReplayStatus &el)
{
+43 -13
View File
@@ -404,16 +404,9 @@ struct SDObject
data.basic.u = 0;
}
~SDObject()
{
for(size_t i = 0; i < data.children.size(); i++)
delete data.children[i];
data.children.clear();
}
~SDObject() { DeleteChildren(); }
DOCUMENT("Create a deep copy of this object.");
SDObject *Duplicate()
SDObject *Duplicate() const
{
SDObject *ret = new SDObject();
ret->name = name;
@@ -437,6 +430,32 @@ struct SDObject
DOCUMENT("The :class:`SDObjectData` with the contents of this object.");
SDObjectData data;
DOCUMENT("Checks if the given object has the same value as this one.");
bool HasEqualValue(const SDObject *o) const
{
bool ret = true;
if(data.str != o->data.str)
{
ret = false;
}
else if(data.basic.u != o->data.basic.u)
{
ret = false;
}
else if(data.children.size() != o->data.children.size())
{
ret = false;
}
else
{
for(size_t c = 0; c < o->data.children.size(); c++)
ret &= data.children[c]->HasEqualValue(o->data.children[c]);
}
return ret;
}
DOCUMENT("Add a new child object by duplicating it.");
inline void AddChild(SDObject *child) { data.children.push_back(child->Duplicate()); }
DOCUMENT("Find a child object by a given name.");
@@ -456,6 +475,15 @@ struct SDObject
return NULL;
}
DOCUMENT("Delete all child objects.");
inline void DeleteChildren()
{
for(size_t i = 0; i < data.children.size(); i++)
delete data.children[i];
data.children.clear();
}
DOCUMENT("Get the number of child objects.");
inline size_t NumChildren() const { return data.children.size(); }
DOCUMENT("Get a ``list`` of :class:`SDObject` children.");
@@ -479,7 +507,7 @@ struct SDObject
inline double AsDouble() const { return data.basic.d; }
inline float AsFloat() const { return (float)data.basic.d; }
inline char AsChar() const { return data.basic.c; }
inline rdcstr AsString() const { return data.str; }
inline const rdcstr &AsString() const { return data.str; }
inline uint64_t AsUInt64() const { return (uint64_t)data.basic.u; }
inline int64_t AsInt64() const { return (int64_t)data.basic.i; }
inline uint32_t AsUInt32() const { return (uint32_t)data.basic.u; }
@@ -554,6 +582,7 @@ struct SDObject
return this;
}
void AddAndOwnChild(SDObject *child) { data.children.push_back(child); }
#endif
// these are common to both python and C++
@@ -780,11 +809,11 @@ inline SDObject *makeSDBool(const char *name, bool val)
}
DOCUMENT("Make a structured object out of a string");
inline SDObject *makeSDString(const char *name, const char *val)
inline SDObject *makeSDString(const char *name, const rdcstr &val)
{
SDObject *ret = new SDObject(name, "string"_lit);
ret->type.basetype = SDBasic::String;
ret->type.byteSize = strlen(val);
ret->type.byteSize = val.size();
ret->data.str = val;
return ret;
}
@@ -848,6 +877,7 @@ SDOBJECT_MAKER(uint32_t, makeSDUInt32);
SDOBJECT_MAKER(float, makeSDFloat);
SDOBJECT_MAKER(bool, makeSDBool);
SDOBJECT_MAKER(const char *, makeSDString);
SDOBJECT_MAKER(const rdcstr &, makeSDString);
SDOBJECT_MAKER(ResourceId, makeSDResourceId);
#undef SDOBJECT_MAKER
@@ -887,7 +917,7 @@ struct SDChunk : public SDObject
SDChunkMetaData metadata;
DOCUMENT("Create a deep copy of this chunk.");
SDChunk *Duplicate()
SDChunk *Duplicate() const
{
SDChunk *ret = new SDChunk();
ret->name = name;
+4
View File
@@ -454,6 +454,8 @@ void RenderDoc::Initialise()
// information to stdout/stderr and being piped around and processed!
if(IsReplayApp())
RDCLOGOUTPUT();
ProcessConfig();
}
RenderDoc::~RenderDoc()
@@ -494,6 +496,8 @@ RenderDoc::~RenderDoc()
m_RemoteThread = 0;
}
delete m_Config;
Process::Shutdown();
Network::Shutdown();
+13 -4
View File
@@ -39,6 +39,7 @@
class Chunk;
struct RDCThumb;
struct ReplayOptions;
struct SDObject;
// not provided by tinyexr, just do by hand
bool is_exr_file(FILE *f);
@@ -418,11 +419,15 @@ public:
void RegisterShutdownFunction(ShutdownFunction func);
void SetReplayApp(bool replay) { m_Replay = replay; }
bool IsReplayApp() const { return m_Replay; }
const rdcstr &GetConfigSetting(rdcstr name) { return m_ConfigSettings[name]; }
void SetConfigSetting(rdcstr name, rdcstr value) { m_ConfigSettings[name] = value; }
void BecomeRemoteServer(const char *listenhost, uint16_t port, RENDERDOC_KillCallback killReplay,
RENDERDOC_PreviewWindowCallback previewWindow);
const SDObject *GetConfigSetting(const rdcstr &name);
SDObject *SetConfigSetting(const rdcstr &name);
void SaveConfigSettings();
void RegisterSetting(const rdcstr &settingPath, SDObject *setting);
DriverInformation GetDriverInformation(GraphicsAPI api);
// can't be disabled, only enabled then latched
@@ -621,8 +626,6 @@ private:
Threading::CriticalSection m_ChildLock;
rdcarray<rdcpair<uint32_t, uint32_t> > m_Children;
std::map<rdcstr, rdcstr> m_ConfigSettings;
std::map<RDCDriver, ReplayDriverProvider> m_ReplayDriverProviders;
std::map<RDCDriver, RemoteDriverProvider> m_RemoteDriverProviders;
@@ -698,6 +701,12 @@ private:
static void TargetControlClientThread(uint32_t version, Network::Socket *client);
ICrashHandler *m_ExHandler;
void ProcessConfig();
SDObject *FindConfigSetting(const rdcstr &name);
SDObject *m_Config = NULL;
};
struct DriverRegistration
+24 -23
View File
@@ -29,6 +29,7 @@
#include "api/replay/renderdoc_replay.h"
#include "api/replay/version.h"
#include "core/core.h"
#include "core/settings.h"
#include "os/os_specific.h"
#include "replay/replay_controller.h"
#include "serialise/rdcfile.h"
@@ -36,11 +37,12 @@
#include "strings/string_utils.h"
#include "replay_proxy.h"
#if ENABLED(RDOC_DEVEL)
static const uint32_t RemoteServerTimeoutMS = 5000;
#endif
RDOC_CONFIG(uint32_t, RemoteServer_TimeoutMS, 5000,
"Timeout in milliseconds for remote server operations.");
#define DEBUG_REMOTE_SERVER OPTION_OFF
RDOC_DEBUG_CONFIG(bool, RemoteServer_DebugLogging, false,
"Where possible (i.e. it is completely unambiguous) replace register names with "
"high-level variable names.");
static const uint32_t RemoteServerProtocolVersion =
uint32_t(RENDERDOC_VERSION_MAJOR * 1000) | RENDERDOC_VERSION_MINOR;
@@ -215,9 +217,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData,
Network::Socket *&client = threadData->socket;
#if ENABLED(RDOC_DEVEL)
client->SetTimeout(RemoteServerTimeoutMS);
#endif
client->SetTimeout(RemoteServer_TimeoutMS);
uint32_t ip = client->GetRemoteIP();
@@ -1156,9 +1156,7 @@ RENDERDOC_CreateRemoteServerConnection(const char *URL, IRemoteServer **rend)
uint32_t version = RemoteServerProtocolVersion;
#if ENABLED(RDOC_DEVEL)
sock->SetTimeout(RemoteServerTimeoutMS);
#endif
sock->SetTimeout(RemoteServer_TimeoutMS);
{
WriteSerialiser ser(new StreamWriter(sock, Ownership::Nothing), Ownership::Stream);
@@ -1218,22 +1216,25 @@ RemoteServer::RemoteServer(Network::Socket *sock, const rdcstr &deviceID)
reader = new ReadSerialiser(new StreamReader(sock, Ownership::Nothing), Ownership::Stream);
writer = new WriteSerialiser(new StreamWriter(sock, Ownership::Nothing), Ownership::Stream);
#if ENABLED(RDOC_DEVEL) && ENABLED(DEBUG_REMOTE_SERVER)
reader->ConfigureStructuredExport(&GetRemoteServerChunkName, false);
writer->ConfigureStructuredExport(&GetRemoteServerChunkName, false);
if(RemoteServer_DebugLogging)
{
reader->ConfigureStructuredExport(&GetRemoteServerChunkName, false);
writer->ConfigureStructuredExport(&GetRemoteServerChunkName, false);
rdcstr filename = FileIO::GetTempFolderFilename() + "/RenderDoc/RemoteServer.log";
rdcstr filename = FileIO::GetTempFolderFilename() + "/RenderDoc/RemoteServer.log";
// truncate the log
debugLog = FileIO::logfile_open(filename.c_str());
FileIO::logfile_close(debugLog, filename.c_str());
debugLog = FileIO::logfile_open(filename.c_str());
// truncate the log
debugLog = FileIO::logfile_open(filename.c_str());
FileIO::logfile_close(debugLog, filename.c_str());
debugLog = FileIO::logfile_open(filename.c_str());
reader->EnableDumping(debugLog);
writer->EnableDumping(debugLog);
#else
debugLog = NULL;
#endif
reader->EnableDumping(debugLog);
writer->EnableDumping(debugLog);
}
else
{
debugLog = NULL;
}
writer->SetStreamingMode(true);
reader->SetStreamingMode(true);
+595
View File
@@ -0,0 +1,595 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "settings.h"
#include "api/replay/structured_data.h"
#include "common/formatting.h"
#include "serialise/streamio.h"
#include "core.h"
#include "3rdparty/pugixml/pugixml.hpp"
static const rdcliteral debugOnlyString = "DEBUG VARIABLE: Read-only in stable builds."_lit;
static rdcstr valueString(const SDObject *o)
{
if(o->type.basetype == SDBasic::String)
return o->data.str;
if(o->type.basetype == SDBasic::UnsignedInteger)
return StringFormat::Fmt("%llu", o->data.basic.u);
if(o->type.basetype == SDBasic::SignedInteger)
return StringFormat::Fmt("%lld", o->data.basic.i);
if(o->type.basetype == SDBasic::Float)
return StringFormat::Fmt("%lf", o->data.basic.d);
if(o->type.basetype == SDBasic::Boolean)
return o->data.basic.b ? "True" : "False";
if(o->type.basetype == SDBasic::Array)
return StringFormat::Fmt("[%zu]", o->NumChildren());
return "{}";
}
struct xml_stream_writer : pugi::xml_writer
{
StreamWriter &stream;
xml_stream_writer(StreamWriter &writer) : stream(writer) {}
void write(const void *data, size_t size) { stream.Write(data, size); }
};
static SDObject *makeSDObject(const char *name, SDBasic type, pugi::xml_node &value)
{
switch(type)
{
case SDBasic::UnsignedInteger: return makeSDObject(name, (uint64_t)value.text().as_ullong());
case SDBasic::SignedInteger: return makeSDObject(name, (int64_t)value.text().as_llong());
case SDBasic::String: return makeSDObject(name, value.text().as_string());
case SDBasic::Float: return makeSDObject(name, value.text().as_float());
case SDBasic::Boolean: return makeSDObject(name, value.text().as_bool());
case SDBasic::Character: return makeSDObject(name, value.text().as_string()[0]);
default: break;
}
return NULL;
}
static void saveSDObject(SDObject &value, pugi::xml_node obj)
{
switch(value.type.basetype)
{
case SDBasic::Resource:
case SDBasic::Enum:
case SDBasic::UnsignedInteger: obj.text() = value.data.basic.u; break;
case SDBasic::SignedInteger: obj.text() = value.data.basic.i; break;
case SDBasic::String: obj.text() = value.data.str.c_str(); break;
case SDBasic::Float: obj.text() = value.data.basic.d; break;
case SDBasic::Boolean: obj.text() = value.data.basic.b; break;
case SDBasic::Character:
{
char str[2] = {value.data.basic.c, '\0'};
obj.text().set(str);
break;
}
default: RDCERR("Unexpected case");
}
}
static void Config2XML(pugi::xml_node &parent, SDObject &child)
{
pugi::xml_node obj = parent.append_child(child.name.c_str());
if(child.type.name == "category"_lit)
{
for(size_t i = 0; i < child.NumChildren(); i++)
Config2XML(obj, *child.GetChild(i));
}
else
{
SDObject *value = child.FindChild("value");
parent.insert_child_before(pugi::node_comment, obj)
.set_value((" " + child.FindChild("description")->data.str + " ").c_str());
obj.append_attribute("type") = ToStr(value->type.basetype).c_str();
if(value->type.basetype == SDBasic::Array)
{
if(!value->data.children.empty())
obj.append_attribute("elemtype") = ToStr(value->data.children[0]->type.basetype).c_str();
else
obj.append_attribute("elemtype") = "";
for(size_t o = 0; o < value->data.children.size(); o++)
saveSDObject(*value->data.children[o], obj.append_child("item"));
}
else
{
saveSDObject(*value, obj);
}
}
}
static SDObject *XML2Config(pugi::xml_node &obj)
{
SDObject *ret = new SDObject(obj.name(), obj.attribute("type") ? "setting"_lit : "category"_lit);
if(ret->type.name == "category"_lit)
{
uint32_t i = 0;
for(pugi::xml_node child = obj.first_child(); child; child = child.next_sibling())
{
if(child.type() == pugi::node_comment)
continue;
SDObject *childObj = XML2Config(child);
if(childObj)
{
ret->data.children.push_back(childObj);
}
else
{
RDCERR("Error converting child %u config option '%s'", i, ret->name.c_str());
delete ret;
return NULL;
}
i++;
}
}
else
{
pugi::xml_node value = obj.first_child();
rdcstr description = obj.previous_sibling().value();
description.trim();
ret->AddAndOwnChild(makeSDObject("description", description));
SDObject *valueObj = NULL;
const SDBasic types[] = {
SDBasic::Array, SDBasic::String, SDBasic::UnsignedInteger,
SDBasic::SignedInteger, SDBasic::Float, SDBasic::Boolean,
};
static rdcarray<rdcstr> basicTypeStrings;
for(SDBasic t : types)
basicTypeStrings.push_back(ToStr(t));
SDBasic type = types[basicTypeStrings.indexOf(obj.attribute("type").as_string())];
if(type == SDBasic::Array)
{
type = types[basicTypeStrings.indexOf(obj.attribute("elemtype").as_string())];
valueObj = makeSDArray("value");
uint32_t i = 0;
for(pugi::xml_node el = value.first_child(); el; el = el.next_sibling())
{
SDObject *childObj = makeSDObject("$el", type, el);
if(childObj)
{
valueObj->data.children.push_back(childObj);
}
else
{
RDCERR("Error converting array value %u in config option '%s'", i, ret->name.c_str());
delete valueObj;
delete ret;
return NULL;
}
i++;
}
}
else
{
valueObj = makeSDObject("value", type, value);
if(!valueObj)
{
RDCERR("Unexpected type %u of attribute %s", type, ret->name.c_str());
delete ret;
return NULL;
}
}
ret->AddAndOwnChild(valueObj);
}
return ret;
}
static SDObject *importXMLConfig(StreamReader &stream)
{
rdcstr buf;
buf.resize((size_t)stream.GetSize());
stream.Read(buf.data(), buf.size());
pugi::xml_document doc;
doc.load_string(buf.c_str(), pugi::parse_default | pugi::parse_comments);
pugi::xml_node root = doc.child("config");
SDObject *ret = new SDObject("config"_lit, "config"_lit);
if(root)
{
for(pugi::xml_node child = root.first_child(); child; child = child.next_sibling())
{
SDObject *childObj = XML2Config(child);
if(childObj)
ret->data.children.push_back(XML2Config(child));
}
}
return ret;
}
static void exportXMLConfig(StreamWriter &stream, const SDObject *obj)
{
pugi::xml_document doc;
pugi::xml_node xRoot = doc.append_child("config");
xRoot.append_attribute("version") = (uint32_t)1;
for(size_t o = 0; o < obj->data.children.size(); o++)
Config2XML(xRoot, *obj->data.children[o]);
xml_stream_writer writer(stream);
doc.save(writer, " ", pugi::format_default | pugi::format_no_empty_element_tags);
}
static bool MergeConfigValues(const rdcstr &prefix, SDObject *dstConfig, const SDObject *srcConfig,
bool updateDescs)
{
bool ret = false;
// for every child in the destination, see if it has a source node. If not, we're out of date
for(size_t i = 0; i < dstConfig->NumChildren(); i++)
ret |= (srcConfig->FindChild(dstConfig->GetChild(i)->name.c_str()) == NULL);
// for every child in the source
for(size_t i = 0; i < srcConfig->NumChildren(); i++)
{
// see if it's present in the destination
const SDObject *srcChild = srcConfig->GetChild(i);
SDObject *dstChild = dstConfig->FindChild(srcChild->name.c_str());
if(dstChild)
{
// if present, merge the values
rdcstr prefixedChild = prefix + dstChild->name;
if(dstChild->type.name == "category"_lit)
{
// recurse if this child is not a setting node
ret |= MergeConfigValues(prefixedChild + ".", dstChild, srcChild, updateDescs);
}
else
{
SDObject *dstVal = dstChild->FindChild("value");
const SDObject *srcVal = srcChild->FindChild("value");
SDObject *dstDesc = dstChild->FindChild("description");
const SDObject *srcDesc = srcChild->FindChild("description");
bool customised = !srcVal->HasEqualValue(dstVal);
// otherwise see if the value is customised, and if so log the change
if(customised)
{
rdcstr oldVal = valueString(dstVal);
rdcstr newVal = valueString(srcVal);
RDCLOG("%s has been customised from %s to %s", (prefix + dstChild->name).c_str(),
oldVal.c_str(), newVal.c_str());
if(dstDesc->data.str.contains(debugOnlyString))
{
RDCWARN("%s customisation will not apply - read only in this build",
(prefix + dstChild->name).c_str());
}
// always set the value. For a debug-only setting this will do nothing but we want to
// update our config value with the user's in case we're going to write out some new
// values/descriptions
dstVal->data.str = srcVal->data.str;
memcpy(&dstVal->data.basic, &srcVal->data.basic, sizeof(dstVal->data.basic));
dstVal->DeleteChildren();
for(size_t c = 0; c < srcVal->data.children.size(); c++)
dstVal->data.children.push_back(srcVal->data.children[c]->Duplicate());
}
// if the description has changed from the loaded, need to write the new one
if(dstDesc->data.str != srcDesc->data.str)
{
if(updateDescs)
dstDesc->data.str = srcDesc->data.str;
ret |= true;
}
}
}
else
{
// child wasn't in the destination config, out of date
ret |= true;
// if we're copying nodes, do that now
dstConfig->AddChild(srcChild->Duplicate());
}
}
return ret;
}
const bool &ConfigVarRegistration<bool>::value()
{
// avoid warnings on stupid compilers
(void)tmp;
return obj->data.basic.b;
}
const uint64_t &ConfigVarRegistration<uint64_t>::value()
{
(void)tmp;
return obj->data.basic.u;
}
const uint32_t &ConfigVarRegistration<uint32_t>::value()
{
tmp = obj->data.basic.u & 0xFFFFFFFFU;
return tmp;
}
const rdcstr &ConfigVarRegistration<rdcstr>::value()
{
(void)tmp;
return obj->data.str;
}
template <typename T>
rdcstr DefValString(const T &el)
{
return ToStr(el);
}
// this one needs a special implementation unfortunately to convert
const rdcarray<rdcstr> &ConfigVarRegistration<rdcarray<rdcstr>>::value()
{
tmp.resize(obj->data.children.size());
for(size_t i = 0; i < tmp.size(); i++)
tmp[i] = obj->data.children[i]->data.str;
return tmp;
}
rdcstr DefValString(const rdcarray<rdcstr> &el)
{
rdcstr ret = "[";
for(size_t i = 0; i < el.size(); i++)
{
if(i != 0)
ret += ", ";
ret += el[i];
}
ret += "]";
return ret;
}
inline SDObject *makeSDObject(const char *name, const rdcarray<rdcstr> &vals)
{
SDObject *ret = new SDObject(name, "array"_lit);
ret->type.basetype = SDBasic::Array;
for(const rdcstr &s : vals)
ret->data.children.push_back(makeSDObject("$el", s));
return ret;
}
#define CONFIG_SUPPORT_TYPE(T) \
ConfigVarRegistration<T>::ConfigVarRegistration(rdcliteral name, const T &defaultValue, \
bool debugOnly, rdcliteral description) \
{ \
rdcstr settingName = name; \
settingName = settingName.substr(settingName.find_last_of("_") + 1); \
\
rdcstr desc = name; \
desc += "\n\n"; \
for(char &c : desc) \
if(c == '_') \
c = '.'; \
desc += description; \
\
desc += "\n\nDefault value: '" + DefValString(defaultValue) + "'"; \
if(debugOnly) \
{ \
desc += "\n"; \
desc += debugOnlyString; \
} \
\
SDObject *setting = new SDObject(settingName, "setting"_lit); \
setting->AddAndOwnChild(makeSDObject("value", defaultValue)); \
setting->AddAndOwnChild(makeSDObject("key", name)); \
setting->AddAndOwnChild(makeSDObject("default", defaultValue)); \
setting->AddAndOwnChild(makeSDObject("description", desc.c_str())); \
\
obj = setting->GetChild(0); \
\
RenderDoc::Inst().RegisterSetting(name, setting); \
}
CONFIG_SUPPORT_TYPE(bool)
CONFIG_SUPPORT_TYPE(uint64_t)
CONFIG_SUPPORT_TYPE(uint32_t)
CONFIG_SUPPORT_TYPE(rdcstr)
CONFIG_SUPPORT_TYPE(rdcarray<rdcstr>)
void RenderDoc::ProcessConfig()
{
rdcstr confFile = FileIO::GetAppFolderFilename("renderdoc.conf");
SDObject *loadedConfig = NULL;
{
StreamReader reader(FileIO::fopen(confFile.c_str(), "rb"));
loadedConfig = importXMLConfig(reader);
}
// iterate through the current config, and update any values that are found in the loaded config.
// returns true if the loaded config is out of date (i.e. there's a value we have which isn't
// present at all, or the descriptions in the loaded config are old).
bool outofDate = ::MergeConfigValues(rdcstr(), m_Config, loadedConfig, false);
// in the replay application, write it back out again if it's out of date. This
// refreshes the config without changing any customised values and means the user can always edit
// the files on disk
if(IsReplayApp() && outofDate)
{
bool success = false;
// merge the current config into the loaded config. Values that overlap will have been updated
// with the user's values above, so all that's left is to add new values which aren't in the
// config or update descriptions
MergeConfigValues(rdcstr(), loadedConfig, m_Config, true);
{
StreamWriter writer(FileIO::fopen((confFile + ".tmp").c_str(), "wb"), Ownership::Stream);
exportXMLConfig(writer, loadedConfig);
// only overwrite the config if there were no errors here
success = !writer.IsErrored();
}
// if we successfully wrote the file, move it over the original
if(success)
FileIO::Move((confFile + ".tmp").c_str(), confFile.c_str(), true);
}
// delete the loaded config if we have it
delete loadedConfig;
}
void RenderDoc::SaveConfigSettings()
{
if(IsReplayApp())
{
rdcstr confFile = FileIO::GetAppFolderFilename("renderdoc.conf");
bool success = false;
{
StreamWriter writer(FileIO::fopen((confFile + ".tmp").c_str(), "wb"), Ownership::Stream);
exportXMLConfig(writer, m_Config);
// only overwrite the config if there were no errors here
success = !writer.IsErrored();
}
// if we successfully wrote the file, move it over the original
if(success)
FileIO::Move((confFile + ".tmp").c_str(), confFile.c_str(), true);
}
}
const SDObject *RenderDoc::GetConfigSetting(const rdcstr &settingPath)
{
return FindConfigSetting(settingPath);
}
SDObject *RenderDoc::SetConfigSetting(const rdcstr &settingPath)
{
return FindConfigSetting(settingPath);
}
SDObject *RenderDoc::FindConfigSetting(const rdcstr &settingPath)
{
if(settingPath.empty())
return m_Config;
SDObject *cur = m_Config;
rdcstr path = settingPath;
int idx = path.find_first_of("_.");
while(idx >= 0)
{
rdcstr node = path.substr(0, idx);
path.erase(0, idx + 1);
SDObject *child = cur->FindChild(node.c_str());
if(!child)
return NULL;
cur = child;
idx = path.find_first_of("_.");
}
SDObject *obj = cur->FindChild(path.c_str());
if(obj)
return obj->FindChild("value");
return NULL;
}
void RenderDoc::RegisterSetting(const rdcstr &settingPath, SDObject *setting)
{
SDObject *cur = m_Config;
if(cur == NULL)
cur = m_Config = new SDObject("config"_lit, "config"_lit);
rdcstr path = settingPath;
int idx = path.indexOf('_');
while(idx >= 0)
{
rdcstr node = path.substr(0, idx);
path.erase(0, idx + 1);
SDObject *child = cur->FindChild(node.c_str());
if(!child)
{
child = new SDObject(node, "category"_lit);
auto it =
std::lower_bound(cur->data.children.begin(), cur->data.children.end(), child,
[](const SDObject *a, const SDObject *b) { return a->name < b->name; });
cur->data.children.insert(it - cur->data.children.begin(), child);
}
cur = child;
idx = path.indexOf('_');
}
SDObject *obj = cur->FindChild(path.c_str());
if(obj != NULL)
RDCFATAL("Duplicate setting %s", settingPath.c_str());
cur->AddAndOwnChild(setting);
}
+79
View File
@@ -0,0 +1,79 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include "api/replay/rdcarray.h"
#include "api/replay/rdcstr.h"
#include "api/replay/version.h"
#include "common/common.h"
struct SDObject;
template <typename T>
struct ConfigVarRegistration;
#define CONFIG_SUPPORT_TYPE(T) \
template <> \
struct ConfigVarRegistration<T> \
{ \
ConfigVarRegistration(rdcliteral name, const T &defaultValue, bool debugOnly, \
rdcliteral description); \
const T &value(); \
\
private: \
SDObject *obj; \
T tmp; \
};
CONFIG_SUPPORT_TYPE(rdcstr);
CONFIG_SUPPORT_TYPE(bool);
CONFIG_SUPPORT_TYPE(uint64_t);
CONFIG_SUPPORT_TYPE(uint32_t);
CONFIG_SUPPORT_TYPE(rdcarray<rdcstr>);
#undef CONFIG_SUPPORT_TYPE
#define RDOC_CONFIG(type, name, defaultValue, description) \
static ConfigVarRegistration<type> CONCAT(config, __LINE__)( \
STRING_LITERAL(STRINGIZE(name)), defaultValue, false, STRING_LITERAL(description)); \
static const type &name = CONCAT(config, __LINE__).value();
// debug configs get set to constants in official stable builds, they will remain configurable
// in nightly builds and of course in development builds
#if RENDERDOC_STABLE_BUILD
#define RDOC_DEBUG_CONFIG(type, name, defaultValue, description) \
static ConfigVarRegistration<type> CONCAT(config, __LINE__)( \
STRING_LITERAL(STRINGIZE(name)), defaultValue, true, STRING_LITERAL(description)); \
static constexpr type name = defaultValue;
#else
#define RDOC_DEBUG_CONFIG(type, name, defaultValue, description) \
ConfigVarRegistration<type> CONCAT(config, __LINE__)( \
STRING_LITERAL(STRINGIZE(name)), defaultValue, true, STRING_LITERAL(description)); \
static const type &name = CONCAT(config, __LINE__).value();
#endif
+9 -3
View File
@@ -25,6 +25,7 @@
#include "d3d11_device.h"
#include "core/core.h"
#include "core/settings.h"
#include "driver/dxgi/dxgi_wrapped.h"
#include "jpeg-compressor/jpge.h"
#include "maths/formatpacking.h"
@@ -38,6 +39,9 @@
#include "d3d11_resources.h"
#include "d3d11_shader_cache.h"
RDOC_CONFIG(rdcarray<rdcstr>, DXBC_Debug_SearchDirPaths, {},
"Paths to search for separated shader debug PDBs.");
WRAPPED_POOL_INST(WrappedID3D11Device);
WrappedID3D11Device *WrappedID3D11Device::m_pCurrentWrappedDevice = NULL;
@@ -112,9 +116,6 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara
D3D11MarkerRegion::device = this;
rdcstr shaderSearchPathString = RenderDoc::Inst().GetConfigSetting("shader.debug.searchPaths");
split(shaderSearchPathString, m_ShaderSearchPaths, ';');
ResourceIDGen::SetReplayResourceIDs();
}
else
@@ -789,6 +790,11 @@ rdcstr WrappedID3D11Device::GetChunkName(uint32_t idx)
return ToStr((D3D11Chunk)idx);
}
const rdcarray<rdcstr> *WrappedID3D11Device::GetShaderDebugInfoSearchPaths()
{
return &DXBC_Debug_SearchDirPaths;
}
void WrappedID3D11Device::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src,
rdcstr d)
{
+1 -3
View File
@@ -314,8 +314,6 @@ private:
D3D11ShaderCache *m_ShaderCache = NULL;
D3D11ResourceManager *m_ResourceManager = NULL;
rdcarray<rdcstr> m_ShaderSearchPaths;
D3D11InitParams m_InitParams;
uint64_t m_SectionVersion;
ReplayOptions m_ReplayOptions;
@@ -456,7 +454,7 @@ public:
return m_LayoutDescs[layout];
}
rdcarray<rdcstr> *GetShaderDebugInfoSearchPaths() { return &m_ShaderSearchPaths; }
const rdcarray<rdcstr> *GetShaderDebugInfoSearchPaths();
template <typename SerialiserType>
bool Serialise_CaptureScope(SerialiserType &ser);
+5 -1
View File
@@ -24,6 +24,7 @@
******************************************************************************/
#include "d3d11_replay.h"
#include "core/settings.h"
#include "driver/dx/official/d3dcompiler.h"
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/intel/intel_counters.h"
@@ -42,6 +43,9 @@
#include "data/hlsl/hlsl_cbuffers.h"
RDOC_CONFIG(bool, D3D11_HardwareCounters, true,
"Enable support for IHV-specific hardware counters on D3D11.");
static const char *DXBCDisassemblyTarget = "DXBC";
D3D11Replay::D3D11Replay(WrappedID3D11Device *d)
@@ -170,7 +174,7 @@ void D3D11Replay::CreateResources(IDXGIFactory *factory)
m_pDevice->GetShaderCache()->SetCaching(false);
if(!m_Proxy)
if(!m_Proxy && D3D11_HardwareCounters)
{
AMDCounters *countersAMD = NULL;
NVCounters *countersNV = NULL;
+1 -1
View File
@@ -956,7 +956,7 @@ public:
ResourceId m_ID;
rdcstr m_DebugInfoPath;
rdcarray<rdcstr> *m_DebugInfoSearchPaths;
const rdcarray<rdcstr> *m_DebugInfoSearchPaths;
rdcarray<byte> m_Bytecode;
+9 -6
View File
@@ -24,6 +24,7 @@
#include "d3d12_replay.h"
#include "core/plugins.h"
#include "core/settings.h"
#include "driver/dx/official/d3dcompiler.h"
#include "driver/dxgi/dxgi_common.h"
#include "driver/ihv/amd/amd_counters.h"
@@ -41,6 +42,12 @@
#include "data/hlsl/hlsl_cbuffers.h"
RDOC_CONFIG(bool, D3D12_ShaderDebugging, false,
"BETA: Enable experimental shader debugging support.");
RDOC_CONFIG(bool, D3D12_HardwareCounters, true,
"Enable support for IHV-specific hardware counters on D3D12.");
static const char *LiveDriverDisassemblyTarget = "Live driver disassembly";
ID3DDevice *GetD3D12DeviceIfAlloc(IUnknown *dev);
@@ -151,7 +158,7 @@ void D3D12Replay::CreateResources()
m_PixelPick.Init(m_pDevice, m_DebugManager);
m_Histogram.Init(m_pDevice, m_DebugManager);
if(!m_Proxy)
if(!m_Proxy && D3D12_HardwareCounters)
{
AMDCounters *counters = NULL;
@@ -271,11 +278,7 @@ APIProperties D3D12Replay::GetAPIProperties()
ret.shadersMutable = false;
ret.rgpCapture =
m_DriverInfo.vendor == GPUVendor::AMD && m_RGP != NULL && m_RGP->DriverSupportsInterop();
// Enable shader debugging if specified in the config
rdcstr setting = strlower(RenderDoc::Inst().GetConfigSetting("d3d12ShaderDebugging"));
if(!strcmp(setting.c_str(), "true") || setting == "1")
ret.shaderDebugging = true;
ret.shaderDebugging = D3D12_ShaderDebugging;
return ret;
}
+5 -1
View File
@@ -24,6 +24,7 @@
******************************************************************************/
#include "gl_replay.h"
#include "core/settings.h"
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/intel/intel_gl_counters.h"
#include "maths/matrix.h"
@@ -35,6 +36,9 @@
#define OPENGL 1
#include "data/glsl/glsl_ubos_cpp.h"
RDOC_CONFIG(bool, OpenGL_HardwareCounters, true,
"Enable support for IHV-specific hardware counters on OpenGL.");
static const char *SPIRVDisassemblyTarget = "SPIR-V (RenderDoc)";
GLReplay::GLReplay(WrappedOpenGL *d)
@@ -227,7 +231,7 @@ void GLReplay::SetReplayData(GLWindowingData data)
if(!HasDebugContext())
return;
if(!m_Proxy)
if(!m_Proxy && OpenGL_HardwareCounters)
{
AMDCounters *countersAMD = NULL;
IntelGlCounters *countersIntel = NULL;
+4 -3
View File
@@ -26,8 +26,11 @@
#include "common/common.h"
#include "core/core.h"
#include "core/plugins.h"
#include "core/settings.h"
#include "official/RGP/DevDriverAPI.h"
RDOC_CONFIG(bool, AMD_RGP_Enable, false, "Enable integration with AMD's RGP tool.");
uint64_t MakeTagFromMarker(const char *marker)
{
if(!marker)
@@ -68,9 +71,7 @@ AMDRGPControl::AMDRGPControl()
m_RGPDispatchTable->minorVersion = DEV_DRIVER_API_MINOR_VERSION;
m_RGPContext = NULL;
const bool enabled = RenderDoc::Inst().GetConfigSetting("ExternalTool_RGPIntegration") == "1";
if(!enabled)
if(!AMD_RGP_Enable)
{
RDCLOG("AMD RGP Interop is not enabled");
return;
@@ -26,12 +26,17 @@
#include <math.h>
#include "common/common.h"
#include "core/core.h"
#include "core/settings.h"
#include "serialise/serialiser.h"
#include "strings/string_utils.h"
#include "dxbc_bytecode.h"
#include "dxbc_container.h"
RDOC_CONFIG(bool, DXBC_Disassembly_FriendlyNaming, true,
"Where possible (i.e. it is completely unambiguous) replace register names with "
"high-level variable names.");
namespace DXBCBytecode
{
// little utility function to both document and easily extract an arbitrary mask
@@ -437,7 +442,7 @@ void Program::DisassembleHexDump()
m_Declarations.reserve(numDecls);
const bool friendly = RenderDoc::Inst().GetConfigSetting("Disassembly_FriendlyNaming") != "0";
const bool friendly = DXBC_Disassembly_FriendlyNaming;
while(cur < end)
{
@@ -23,6 +23,7 @@
******************************************************************************/
#include <float.h>
#include "core/settings.h"
#include "driver/shaders/spirv/spirv_editor.h"
#include "driver/shaders/spirv/spirv_op_helpers.h"
#include "vk_core.h"
@@ -30,6 +31,10 @@
#include "vk_replay.h"
#include "vk_shader_cache.h"
RDOC_CONFIG(
bool, Vulkan_BindlessFeedback, true,
"Enable fetching from GPU which descriptors were dynamically used in descriptor arrays.");
struct feedbackData
{
uint64_t offset;
@@ -524,6 +529,9 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId)
if(m_BindlessFeedback.Usage.find(eventId) != m_BindlessFeedback.Usage.end())
return;
if(!Vulkan_BindlessFeedback)
return;
// create it here so we won't re-run any code if the event is re-selected. We'll mark it as valid
// if it actually has any data in it later.
DynamicUsedBinds &result = m_BindlessFeedback.Usage[eventId];
+5 -1
View File
@@ -24,6 +24,7 @@
#include "vk_debug.h"
#include <float.h>
#include "core/settings.h"
#include "data/glsl_shaders.h"
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/amd/official/GPUPerfAPI/Include/GPUPerfAPI-VK.h"
@@ -38,6 +39,9 @@
#define VULKAN 1
#include "data/glsl/glsl_ubos_cpp.h"
RDOC_CONFIG(bool, Vulkan_HardwareCounters, true,
"Enable support for IHV-specific hardware counters on Vulkan.");
const VkDeviceSize STAGE_BUFFER_BYTE_SIZE = 16 * 1024 * 1024ULL;
static void create(WrappedVulkan *driver, const char *objName, const int line, VkSampler *sampler,
@@ -1712,7 +1716,7 @@ void VulkanReplay::CreateResources()
GPA_vkContextOpenInfo context = {Unwrap(m_pDriver->GetInstance()),
Unwrap(m_pDriver->GetPhysDev()), Unwrap(m_pDriver->GetDev())};
if(!m_pDriver->GetReplay()->IsRemoteProxy())
if(!m_pDriver->GetReplay()->IsRemoteProxy() && Vulkan_HardwareCounters)
{
AMDCounters *counters = NULL;
+5 -5
View File
@@ -27,6 +27,7 @@
#include <float.h>
#include <math.h>
#include <algorithm>
#include "core/settings.h"
#include "driver/ihv/amd/amd_rgp.h"
#include "driver/shaders/spirv/spirv_compile.h"
#include "maths/formatpacking.h"
@@ -41,6 +42,9 @@
#define VULKAN 1
#include "data/glsl/glsl_ubos_cpp.h"
RDOC_CONFIG(bool, Vulkan_ShaderDebugging, false,
"BETA: Enable experimental shader debugging support.");
static const char *SPIRVDisassemblyTarget = "SPIR-V (RenderDoc)";
static const char *AMDShaderInfoTarget = "AMD_shader_info";
static const char *KHRExecutablePropertiesTarget = "KHR_pipeline_executable_properties";
@@ -186,11 +190,7 @@ APIProperties VulkanReplay::GetAPIProperties()
ret.shadersMutable = false;
ret.rgpCapture =
m_DriverInfo.vendor == GPUVendor::AMD && m_RGP != NULL && m_RGP->DriverSupportsInterop();
// Enable shader debugging if specified in the config
rdcstr setting = strlower(RenderDoc::Inst().GetConfigSetting("vulkanShaderDebugging"));
if(!strcmp(setting.c_str(), "true") || setting == "1")
ret.shaderDebugging = true;
ret.shaderDebugging = Vulkan_ShaderDebugging;
return ret;
}
+2
View File
@@ -189,6 +189,7 @@
<ClInclude Include="common\timing.h" />
<ClInclude Include="common\wrapped_pool.h" />
<ClInclude Include="core\bit_flag_iterator.h" />
<ClInclude Include="core\settings.h" />
<ClInclude Include="core\core.h" />
<ClInclude Include="core\crash_handler.h" />
<ClInclude Include="core\intervals.h" />
@@ -421,6 +422,7 @@
<ClCompile Include="common\dds_readwrite.cpp" />
<ClCompile Include="common\threading_tests.cpp" />
<ClCompile Include="core\bit_flag_iterator_tests.cpp" />
<ClCompile Include="core\settings.cpp" />
<ClCompile Include="core\core.cpp" />
<ClCompile Include="core\image_viewer.cpp" />
<ClCompile Include="core\intervals_tests.cpp" />
+6
View File
@@ -504,6 +504,9 @@
<ClInclude Include="common\formatting.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="core\settings.h">
<Filter>Core</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="maths\camera.cpp">
@@ -887,6 +890,9 @@
<ClCompile Include="maths\vec.cpp">
<Filter>Common\Maths</Filter>
</ClCompile>
<ClCompile Include="core\settings.cpp">
<Filter>Core</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="os\win32\comexport.def">
+9 -5
View File
@@ -192,15 +192,19 @@ extern "C" RENDERDOC_API uint64_t RENDERDOC_CC RENDERDOC_GetCurrentProcessMemory
return Process::GetMemoryUsage();
}
extern "C" RENDERDOC_API const char *RENDERDOC_CC RENDERDOC_GetConfigSetting(const char *name)
extern "C" RENDERDOC_API const SDObject *RENDERDOC_CC RENDERDOC_GetConfigSetting(const char *name)
{
return RenderDoc::Inst().GetConfigSetting(name).c_str();
return RenderDoc::Inst().GetConfigSetting(name);
}
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetConfigSetting(const char *name,
const char *value)
extern "C" RENDERDOC_API SDObject *RENDERDOC_CC RENDERDOC_SetConfigSetting(const char *name)
{
RenderDoc::Inst().SetConfigSetting(name, value);
return RenderDoc::Inst().SetConfigSetting(name);
}
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SaveConfigSettings()
{
return RenderDoc::Inst().SaveConfigSettings();
}
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetColors(FloatVector darkChecker,
+2 -27
View File
@@ -34,6 +34,8 @@
int64_t Chunk::m_LiveChunks = 0;
int64_t Chunk::m_TotalMem = 0;
#endif
void DumpObject(FileIO::LogFileHandle *log, const rdcstr &indent, SDObject *obj)
{
if(obj->NumChildren() > 0)
@@ -81,8 +83,6 @@ void DumpChunk(bool reading, FileIO::LogFileHandle *log, SDChunk *chunk)
DumpObject(log, " ", chunk);
}
#endif
/////////////////////////////////////////////////////////////
// Read Serialiser functions
@@ -265,12 +265,10 @@ void Serialiser<SerialiserMode::Reading>::EndChunk()
m_StructureStack.pop_back();
}
#if ENABLED(RDOC_DEVEL)
if(m_DebugDumpLog && !m_StructuredFile->chunks.empty())
{
DumpChunk(true, m_DebugDumpLog, m_StructuredFile->chunks.back());
}
#endif
}
// only skip remaining bytes if we have a valid length - if we have a length of 0 we wrote this
@@ -526,12 +524,10 @@ void Serialiser<SerialiserMode::Writing>::EndChunk()
m_StructureStack.pop_back();
}
#if ENABLED(RDOC_DEVEL)
if(m_DebugDumpLog && !m_StructuredFile->chunks.empty())
{
DumpChunk(false, m_DebugDumpLog, m_StructuredFile->chunks.back());
}
#endif
}
// align to the natural chunk alignment
@@ -621,27 +617,6 @@ void Serialiser<SerialiserMode::Writing>::WriteStructuredFile(const SDFile &file
scratchWriter.m_StructuredFile = &scratchWriter.m_StructData;
}
template <>
rdcstr DoStringise(const SDBasic &el)
{
BEGIN_ENUM_STRINGISE(SDBasic);
{
STRINGISE_ENUM_CLASS(Chunk);
STRINGISE_ENUM_CLASS(Struct);
STRINGISE_ENUM_CLASS(Array);
STRINGISE_ENUM_CLASS(Null);
STRINGISE_ENUM_CLASS(Buffer);
STRINGISE_ENUM_CLASS(String);
STRINGISE_ENUM_CLASS(Enum);
STRINGISE_ENUM_CLASS(UnsignedInteger);
STRINGISE_ENUM_CLASS(SignedInteger);
STRINGISE_ENUM_CLASS(Float);
STRINGISE_ENUM_CLASS(Boolean);
STRINGISE_ENUM_CLASS(Character);
}
END_ENUM_STRINGISE();
}
template <>
rdcstr DoStringise(const SDTypeFlags &el)
{
+1 -6
View File
@@ -121,11 +121,8 @@ public:
uint32_t GetChunkMetadataRecording() { return m_ChunkFlags; }
void SetChunkMetadataRecording(uint32_t flags);
// debug-only option to dump out (roughly) the data going through the serialiser as it happens
#if ENABLED(RDOC_DEVEL)
// debug-only option to dump out (roughly) the data going through the serialiser as it happens
void EnableDumping(FileIO::LogFileHandle *debugLog) { m_DebugDumpLog = debugLog; }
#endif
SDChunkMetaData &ChunkMetadata() { return m_ChunkMetadata; }
//////////////////////////////////////////
// Utility functions
@@ -1340,9 +1337,7 @@ private:
}
ChunkLookup m_ChunkLookup = NULL;
#if ENABLED(RDOC_DEVEL)
FileIO::LogFileHandle *m_DebugDumpLog = NULL;
#endif
};
#ifndef SERIALISER_IMPL
+3 -1
View File
@@ -18,7 +18,9 @@ class D3D12_RGP_Capture(rdtest.TestCase):
# Need to enable RGP mode before opening the capture
def run(self):
rd.SetConfigSetting("ExternalTool_RGPIntegration", "1")
obj: rd.SDObject = rd.SetConfigSetting("AMD.RGP.Enable")
if obj is not None:
obj.data.basic.b = True
super().run()
def check_capture(self):
+3 -1
View File
@@ -8,7 +8,9 @@ class VK_RGP_Capture(rdtest.TestCase):
# Need to enable RGP mode before opening the capture
def run(self):
rd.SetConfigSetting("ExternalTool_RGPIntegration", "1")
obj: rd.SDObject = rd.SetConfigSetting("AMD.RGP.Enable")
if obj is not None:
obj.data.basic.b = True
super().run()
def check_capture(self):