From a7778daed25508486c30b081aa359690ead2be68 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 9 Feb 2016 15:45:47 -0800 Subject: [PATCH] Change graphics dialog to persisted dropdowns --- interface/resources/config/render.json | 19 ++++ interface/src/Application.cpp | 2 +- interface/src/ui/PreferencesDialog.cpp | 39 ++++---- .../render-utils/src/AmbientOcclusionEffect.h | 4 +- .../render-utils/src/AntialiasingEffect.h | 4 +- libraries/render-utils/src/RenderShadowTask.h | 4 +- libraries/render/src/render/Engine.cpp | 74 ++++---------- libraries/render/src/render/Engine.h | 21 +--- libraries/render/src/render/Task.h | 96 ++++++++++++++++--- libraries/shared/src/shared/JSONHelpers.cpp | 2 +- 10 files changed, 150 insertions(+), 115 deletions(-) create mode 100644 interface/resources/config/render.json diff --git a/interface/resources/config/render.json b/interface/resources/config/render.json new file mode 100644 index 0000000000..092530d864 --- /dev/null +++ b/interface/resources/config/render.json @@ -0,0 +1,19 @@ +{ + "RenderShadowTask": { + "Enabled": { + "enabled": true + } + }, + "RenderDeferredTask": { + "AmbientOcclusion": { + "Enabled": { + "enabled": true + } + }, + "Antialiasing": { + "Enabled": { + "enabled": true + } + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 829deda139..4c4c515a0d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1156,7 +1156,7 @@ void Application::initializeGL() { render::CullFunctor cullFunctor = LODManager::shouldRender; _renderEngine->addJob("RenderShadowTask", cullFunctor); _renderEngine->addJob("RenderDeferredTask", cullFunctor); - _renderEngine->loadConfig(); + _renderEngine->load(); _renderEngine->registerScene(_main3DScene); // TODO: Load a cached config file diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 94ea732b2c..b1812b7bd3 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -335,37 +335,32 @@ void setupPreferences() { { static const QString RENDER("Graphics"); - auto renderEngine = qApp->getRenderEngine(); - auto renderConfig = renderEngine->getConfiguration(); + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + + auto ambientOcclusionConfig = renderConfig->getConfig(); { - auto getter = [renderEngine]()->QString { return renderEngine->getNamedConfig(); }; - auto setter = [renderEngine](QString config) { renderEngine->setNamedConfig(config); }; - auto preference = new ComboBoxBrowsePreference(RENDER, "Engine Profile", getter, setter); - preference->setItems(renderEngine->getNamedConfigList()); - preference->setBrowseLabel("Custom..."); - preference->setBrowseFilter("Engine Profiles (*.json)"); + auto getter = [ambientOcclusionConfig]()->QString { return ambientOcclusionConfig->getPreset(); }; + auto setter = [ambientOcclusionConfig](QString preset) { ambientOcclusionConfig->setPreset(preset); }; + auto preference = new ComboBoxPreference(RENDER, "Ambient Occlusion", getter, setter); + preference->setItems(ambientOcclusionConfig->getPresetList()); preferences->addPreference(preference); } - // Theses will override the engine profile because they are defined afterwards + auto antialiasingConfig = renderConfig->getConfig(); { - auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; - auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; - auto preference = new CheckPreference(RENDER, "Ambient Occlusion", getter, setter); + auto getter = [antialiasingConfig]()->QString { return antialiasingConfig->getPreset(); }; + auto setter = [antialiasingConfig](QString preset) { antialiasingConfig->setPreset(preset); }; + auto preference = new ComboBoxPreference(RENDER, "Antialiasing", getter, setter); + preference->setItems(antialiasingConfig->getPresetList()); preferences->addPreference(preference); } + auto shadowConfig = renderConfig->getConfig(); { - auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; - auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; - auto preference = new CheckPreference(RENDER, "Antialiasing", getter, setter); - preferences->addPreference(preference); - } - - { - auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled(); }; - auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled(enable); }; - auto preference = new CheckPreference(RENDER, "Shadows", getter, setter); + auto getter = [shadowConfig]()->QString { return shadowConfig->getPreset(); }; + auto setter = [shadowConfig](QString preset) { shadowConfig->setPreset(preset); }; + auto preference = new ComboBoxPreference(RENDER, "Shadows", getter, setter); + preference->setItems(shadowConfig->getPresetList()); preferences->addPreference(preference); } } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index c040e31188..442c108af7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -16,7 +16,7 @@ #include "render/DrawTask.h" -class AmbientOcclusionEffectConfig : public render::Job::Config { +class AmbientOcclusionEffectConfig : public render::Job::PersistentConfig { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) @@ -32,7 +32,7 @@ class AmbientOcclusionEffectConfig : public render::Job::Config { Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) Q_PROPERTY(double gpuTime READ getGpuTime) public: - AmbientOcclusionEffectConfig() : render::Job::Config(false) {} + AmbientOcclusionEffectConfig() : render::Job::PersistentConfig("Ambient Occlusion", false) {} const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 6; diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index f0a32c68ff..000760a2e2 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -16,11 +16,11 @@ #include "render/DrawTask.h" -class AntiAliasingConfig : public render::Job::Config { +class AntiAliasingConfig : public render::Job::PersistentConfig { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled) public: - AntiAliasingConfig() : render::Job::Config(false) {} + AntiAliasingConfig() : render::Job::PersistentConfig("Antialiasing", false) {} }; class Antialiasing { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index e1bf983b79..177a42aff7 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -31,11 +31,11 @@ protected: render::ShapePlumberPointer _shapePlumber; }; -class RenderShadowTaskConfig : public render::Task::Config { +class RenderShadowTaskConfig : public render::Task::PersistentConfig { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) public: - RenderShadowTaskConfig() : render::Task::Config(false) {} + RenderShadowTaskConfig() : render::Task::PersistentConfig("Shadows", false) {} signals: void dirty(); diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 470eeb8822..806c964ec0 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -13,75 +13,39 @@ #include +#include + #include using namespace render; -const QString ENGINE_CONFIG_DEFAULT = "Default"; -// TODO: Presets (e.g., "Highest Performance", "Highest Quality") will go here -const QMap Engine::PRESETS = {}; - Engine::Engine() : - _namedConfig(QStringList() << "Render" << "Engine"), _sceneContext(std::make_shared()), _renderContext(std::make_shared()) { } -QStringList Engine::getNamedConfigList() { - QStringList list; - list << ENGINE_CONFIG_DEFAULT << PRESETS.keys(); - auto current = _namedConfig.get(); - if (!list.contains(current)) { - list << current; - } - - return list; -} - -QString Engine::getNamedConfig() { - return _namedConfig.get(); -} - -void Engine::setNamedConfig(const QString& config) { - _namedConfig.set(config); - loadConfig(); -} - -void Engine::loadConfig() { +void Engine::load() { auto config = getConfiguration(); - QString current = _namedConfig.get(); + const QString configFile= "config/render.json"; - if (_defaultConfig.isNull()) { - // Set the default - _defaultConfig = - QJsonDocument::fromJson(config->toJSON().toUtf8()).object(); - } - - if (current == ENGINE_CONFIG_DEFAULT) { - // Load the default - config->load(_defaultConfig.toObject()); - } else if (PRESETS.contains(current)) { - // Load a preset - config->load(QJsonDocument::fromJson(PRESETS[current].toUtf8()).object()); + QUrl path(PathUtils::resourcesPath() + configFile); + QFile file(path.toString()); + if (!file.exists()) { + qWarning() << "Engine configuration file" << path << "does not exist"; + } else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Engine configuration file" << path << "cannot be opened"; } else { - QFile file(current); - if (!file.exists()) { - qWarning() << "Engine configuration file" << current << "does not exist"; - } else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning() << "Engine configuration file" << current << "cannot be opened"; + QString data = file.readAll(); + file.close(); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &error); + if (error.error == error.NoError) { + config->setPresetList(doc.object()); + qDebug() << "Engine configuration file" << path << "loaded"; } else { - QString data = file.readAll(); - file.close(); - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &error); - if (error.error == error.NoError) { - config->load(doc.object()); - qDebug() << "Engine configuration file" << current << "loaded"; - } else { - qWarning() << "Engine configuration file" << current << "failed to load:" << - error.errorString() << "at offset" << error.offset; - } + qWarning() << "Engine configuration file" << path << "failed to load:" << + error.errorString() << "at offset" << error.offset; } } } diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 238754c46a..1af0e6d76f 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -27,18 +27,9 @@ public: Engine(); ~Engine() = default; - // Get the configurations - QStringList getNamedConfigList(); - - // Get the current named configuration - QString getNamedConfig(); - - // Set a named configuration - void setNamedConfig(const QString& config); - - // Load the current named config - // The first time this is run, it will also set the current configuration as Default - void loadConfig(); + // Load any persisted settings, and set up the presets + // This should be run after adding all jobs, and before building ui + void load(); // Register the scene void registerScene(const ScenePointer& scene) { _sceneContext->_scene = scene; } @@ -52,14 +43,8 @@ public: void run(); protected: - Setting::Handle _namedConfig; - QJsonValue _defaultConfig; - SceneContextPointer _sceneContext; RenderContextPointer _renderContext; - -private: - static const QMap PRESETS; }; using EnginePointer = std::shared_ptr; diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index a47f8e39fe..2d29e5eedd 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -19,6 +19,8 @@ #include #include +#include "SettingHandle.h" + #include "Context.h" #include "gpu/Batch.h" @@ -71,12 +73,24 @@ public: bool alwaysEnabled{ true }; bool enabled{ true }; + virtual void setPresetList(const QJsonObject& object) { + for (auto it = object.begin(); it != object.end(); it++) { + JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); + if (child) { + child->setPresetList(it.value().toObject()); + } + } + } + // This must be named toJSON to integrate with the global scripting JSON object Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } - Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); } + Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } public slots: - void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); } + void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } + +signals: + void loaded(); }; class TaskConfig : public JobConfig { @@ -95,15 +109,6 @@ public: return findChild(name); } - template void setJobEnabled(bool enable = true, std::string job = "") { - assert(getConfig(job)->alwaysEnabled != true); - getConfig(job)->enabled = enable; - refresh(); // trigger a Job->configure - } - template bool isJobEnabled(bool enable = true, std::string job = "") const { - return getConfig(job)->isEnabled(); - } - public slots: void refresh(); @@ -111,6 +116,69 @@ private: Task* _task; }; +template class PersistentConfig : public C { +public: + const QString DEFAULT = "Default"; + const QString NONE = "None"; + + PersistentConfig() = delete; + PersistentConfig(const QString& path) : + _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } + PersistentConfig(const QStringList& path) : + _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } + PersistentConfig(const QString& path, bool enabled) : C(enabled), + _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + PersistentConfig(const QStringList& path, bool enabled) : C(enabled), + _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + + QStringList getPresetList() { + if (_presets.empty()) { + setPresetList(QJsonObject()); + } + return _presets.keys(); + } + + virtual void setPresetList(const QJsonObject& list) override { + assert(_presets.empty()); + + _default = toJsonValue(*this).toObject().toVariantMap(); + + _presets.unite(list.toVariantMap()); + if (alwaysEnabled || enabled) { + _presets.insert(DEFAULT, _default); + } + if (!alwaysEnabled) { + _presets.insert(NONE, QVariantMap{{ "enabled", false }}); + } + + auto preset = _preset.get(); + if (preset != _preset.getDefault() && _presets.contains(preset)) { + // Load the persisted configuration + load(_presets[preset].toMap()); + } + } + + QString getPreset() { return _preset.get(); } + + void setPreset(const QString& preset) { + _preset.set(preset); + if (_presets.contains(preset)) { + // Always start back at default to remain deterministic + QVariantMap config = _default; + QVariantMap presetConfig = _presets[preset].toMap(); + for (auto it = presetConfig.cbegin(); it != presetConfig.cend(); it++) { + config.insert(it.key(), it.value()); + } + load(config); + } + } + +protected: + QVariantMap _default; + QVariantMap _presets; + Setting::Handle _preset; +}; + template void jobConfigure(T& data, const C& configuration) { data.configure(configuration); } @@ -133,6 +201,7 @@ template void jobRun(T& data, const SceneContextPoin class Job { public: using Config = JobConfig; + using PersistentConfig = PersistentConfig; using QConfigPointer = std::shared_ptr; using None = JobNoIO; @@ -220,6 +289,7 @@ public: class Task { public: using Config = TaskConfig; + using PersistentConfig = PersistentConfig; using QConfigPointer = Job::QConfigPointer; using None = Job::None; @@ -273,9 +343,11 @@ public: config->setParent(_config.get()); config->setObjectName(name.c_str()); - // Connect dirty->refresh if defined + // Connect loaded->refresh + QObject::connect(config.get(), SIGNAL(loaded()), _config.get(), SLOT(refresh())); static const char* DIRTY_SIGNAL = "dirty()"; if (config->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { + // Connect dirty->refresh if defined QObject::connect(config.get(), SIGNAL(dirty()), _config.get(), SLOT(refresh())); } diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index e717050055..c7cbf0e724 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -111,7 +111,7 @@ void qObjectFromJsonValue(const QJsonValue& j, QObject& o) { for (auto it = object.begin(); it != object.end(); it++) { std::string key = it.key().toStdString(); if (it.value().isObject()) { - QObject* child = o.findChild(key.c_str(), Qt::FindChildOption::FindDirectChildrenOnly); + QObject* child = o.findChild(key.c_str(), Qt::FindDirectChildrenOnly); if (child) { qObjectFromJsonValue(it.value(), *child); }