Change graphics dialog to persisted dropdowns

This commit is contained in:
Zach Pomerantz 2016-02-09 15:45:47 -08:00
parent 84197e5eb2
commit a7778daed2
10 changed files with 150 additions and 115 deletions

View file

@ -0,0 +1,19 @@
{
"RenderShadowTask": {
"Enabled": {
"enabled": true
}
},
"RenderDeferredTask": {
"AmbientOcclusion": {
"Enabled": {
"enabled": true
}
},
"Antialiasing": {
"Enabled": {
"enabled": true
}
}
}
}

View file

@ -1156,7 +1156,7 @@ void Application::initializeGL() {
render::CullFunctor cullFunctor = LODManager::shouldRender;
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", cullFunctor);
_renderEngine->loadConfig();
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
// TODO: Load a cached config file

View file

@ -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<AmbientOcclusionEffect>();
{
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<Antialiasing>();
{
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<AmbientOcclusionEffect>(); };
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<AmbientOcclusionEffect>(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<RenderShadowTask>();
{
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<Antialiasing>(); };
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<Antialiasing>(enable); };
auto preference = new CheckPreference(RENDER, "Antialiasing", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = [renderConfig]()->bool { return renderConfig->isJobEnabled<RenderShadowTask>(); };
auto setter = [renderConfig](bool enable) { renderConfig->setJobEnabled<RenderShadowTask>(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);
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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();

View file

@ -13,75 +13,39 @@
#include <QtCore/QFile>
#include <PathUtils.h>
#include <gpu/Context.h>
using namespace render;
const QString ENGINE_CONFIG_DEFAULT = "Default";
// TODO: Presets (e.g., "Highest Performance", "Highest Quality") will go here
const QMap<QString, QString> Engine::PRESETS = {};
Engine::Engine() :
_namedConfig(QStringList() << "Render" << "Engine"),
_sceneContext(std::make_shared<SceneContext>()),
_renderContext(std::make_shared<RenderContext>()) {
}
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;
}
}
}

View file

@ -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<QString> _namedConfig;
QJsonValue _defaultConfig;
SceneContextPointer _sceneContext;
RenderContextPointer _renderContext;
private:
static const QMap<QString, QString> PRESETS;
};
using EnginePointer = std::shared_ptr<Engine>;

View file

@ -19,6 +19,8 @@
#include <QtCore/qjsonvalue.h>
#include <shared/JSONHelpers.h>
#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<JobConfig*>(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<typename T::Config*>(name);
}
template <class T> void setJobEnabled(bool enable = true, std::string job = "") {
assert(getConfig<T>(job)->alwaysEnabled != true);
getConfig<T>(job)->enabled = enable;
refresh(); // trigger a Job->configure
}
template <class T> bool isJobEnabled(bool enable = true, std::string job = "") const {
return getConfig<T>(job)->isEnabled();
}
public slots:
void refresh();
@ -111,6 +116,69 @@ private:
Task* _task;
};
template <class C> 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<QString> _preset;
};
template <class T, class C> void jobConfigure(T& data, const C& configuration) {
data.configure(configuration);
}
@ -133,6 +201,7 @@ template <class T, class I, class O> void jobRun(T& data, const SceneContextPoin
class Job {
public:
using Config = JobConfig;
using PersistentConfig = PersistentConfig<Config>;
using QConfigPointer = std::shared_ptr<QObject>;
using None = JobNoIO;
@ -220,6 +289,7 @@ public:
class Task {
public:
using Config = TaskConfig;
using PersistentConfig = PersistentConfig<Config>;
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()));
}

View file

@ -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<QObject*>(key.c_str(), Qt::FindChildOption::FindDirectChildrenOnly);
QObject* child = o.findChild<QObject*>(key.c_str(), Qt::FindDirectChildrenOnly);
if (child) {
qObjectFromJsonValue(it.value(), *child);
}