Merge branch 'master' of https://github.com/highfidelity/hifi into red

This commit is contained in:
samcake 2016-02-12 11:31:47 -08:00
commit 9bfeb4a1d5
19 changed files with 271 additions and 47 deletions

View file

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

View file

@ -49,7 +49,7 @@ Preference {
Button {
id: button
anchors { right: parent.right; verticalCenter: dataTextField.verticalCenter }
text: "Browse"
text: preference.browseLabel
onClicked: {
var browser = fileBrowserBuilder.createObject(desktop, { selectDirectory: true, folder: fileDialogHelper.pathToUrl(preference.value) });
browser.selectedFile.connect(function(fileUrl){

View file

@ -0,0 +1,37 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Preference {
id: root
property real spacing: 8
height: labelText.height + dataComboBox.height + spacing
Component.onCompleted: {
dataComboBox.currentIndex = dataComboBox.find(preference.value);
}
function save() {
preference.value = dataComboBox.currentText;
preference.save();
}
Text {
id: labelText
color: enabled ? "black" : "gray"
text: root.label
}
ComboBox {
id: dataComboBox
model: preference.items
style: ComboBoxStyle { renderType: Text.QtRendering }
anchors {
top: labelText.bottom
left: parent.left
right: parent.right
topMargin: root.spacing
rightMargin: root.spacing
}
}
}

View file

@ -81,6 +81,7 @@ Preference {
property var sliderBuilder: Component { SliderPreference { } }
property var avatarBuilder: Component { AvatarPreference { } }
property var buttonBuilder: Component { ButtonPreference { } }
property var comboBoxBuilder: Component { ComboBoxPreference { } }
property var preferences: []
function buildPreferences() {
@ -123,7 +124,11 @@ Preference {
case Preference.Button:
builder = buttonBuilder;
break
break;
case Preference.ComboBox:
builder = comboBoxBuilder;
break;
};
if (builder) {

View file

@ -7,7 +7,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Preferences"
showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Graphics"]
showCategories: ["Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -0,0 +1,19 @@
import QtQuick 2.5
import Qt.labs.settings 1.0
import "../../dialogs"
PreferencesDialog {
id: root
objectName: "GraphicsPreferencesDialog"
title: "Graphics Preferences"
showCategories: ["Graphics"]
property var settings: Settings {
category: root.objectName
property alias x: root.x
property alias y: root.y
property alias width: root.width
property alias height: root.height
}
}

View file

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

View file

@ -312,6 +312,12 @@ Menu::Menu() {
DependencyManager::get<OffscreenUi>()->toggle(QString("hifi/dialogs/AudioPreferencesDialog.qml"), "AudioPreferencesDialog");
});
// Settings > Graphics...
action = addActionToQMenuAndActionHash(settingsMenu, "Graphics...");
connect(action, &QAction::triggered, [] {
DependencyManager::get<OffscreenUi>()->toggle(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"), "GraphicsPreferencesDialog");
});
// Settings > LOD...-- FIXME: needs implementation
action = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
connect(action, &QAction::triggered, [] {

View file

@ -336,24 +336,31 @@ void setupPreferences() {
{
static const QString RENDER("Graphics");
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
auto ambientOcclusionConfig = renderConfig->getConfig<AmbientOcclusionEffect>();
{
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 = [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);
}
auto antialiasingConfig = renderConfig->getConfig<Antialiasing>();
{
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);
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<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,26 +16,25 @@ void OctreeElementBag::deleteAll() {
_bagElements = Bag();
}
/// does the bag contain elements?
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
bool OctreeElementBag::isEmpty() {
// Pop all expired front elements
while (!_bagElements.empty() && _bagElements.front().expired()) {
_bagElements.pop();
}
return _bagElements.empty();
}
void OctreeElementBag::insert(OctreeElementPointer element) {
_bagElements.push(element);
_bagElements[element.get()] = element;
}
OctreeElementPointer OctreeElementBag::extract() {
OctreeElementPointer result;
// Find the first element still alive
while (!result && !_bagElements.empty()) {
result = _bagElements.front().lock(); // Grab head's shared_ptr
_bagElements.pop();
Bag::iterator it = _bagElements.begin();
while (it != _bagElements.end() && !result) {
result = it->second.lock();
it = _bagElements.erase(it);
}
return result;
}

View file

@ -16,17 +16,22 @@
#ifndef hifi_OctreeElementBag_h
#define hifi_OctreeElementBag_h
#include <queue>
#include <unordered_map>
#include "OctreeElement.h"
class OctreeElementBag {
using Bag = std::queue<OctreeElementWeakPointer>;
using Bag = std::unordered_map<OctreeElement*, OctreeElementWeakPointer>;
public:
void insert(OctreeElementPointer element); // put a element into the bag
OctreeElementPointer extract(); // pull a element out of the bag (could come in any order)
bool isEmpty();
OctreeElementPointer extract(); /// pull a element out of the bag (could come in any order) and if all of the
/// elements have expired, a single null pointer will be returned
bool isEmpty(); /// does the bag contain elements,
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
void deleteAll();

View file

@ -16,7 +16,7 @@
#include "render/DrawTask.h"
class AmbientOcclusionEffectConfig : public render::Job::Config {
class AmbientOcclusionEffectConfig : public render::Job::Config::Persistent {
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::Config::Persistent("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::Config::Persistent {
Q_OBJECT
Q_PROPERTY(bool enabled MEMBER enabled)
public:
AntiAliasingConfig() : render::Job::Config(false) {}
AntiAliasingConfig() : render::Job::Config::Persistent("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::Config::Persistent {
Q_OBJECT
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty)
public:
RenderShadowTaskConfig() : render::Task::Config(false) {}
RenderShadowTaskConfig() : render::Task::Config::Persistent("Shadows", false) {}
signals:
void dirty();

View file

@ -9,9 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Engine.h"
#include <QtCore/QFile>
#include <PathUtils.h>
#include <gpu/Context.h>
#include "Engine.h"
using namespace render;
@ -20,6 +25,31 @@ Engine::Engine() :
_renderContext(std::make_shared<RenderContext>()) {
}
void Engine::load() {
auto config = getConfiguration();
const QString configFile= "config/render.json";
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 {
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 {
qWarning() << "Engine configuration file" << path << "failed to load:" <<
error.errorString() << "at offset" << error.offset;
}
}
}
void Engine::run() {
// Sync GPU state before beginning to render
_renderContext->args->_context->syncCache();

View file

@ -12,6 +12,8 @@
#ifndef hifi_render_Engine_h
#define hifi_render_Engine_h
#include <SettingHandle.h>
#include "Context.h"
#include "Task.h"
@ -25,6 +27,10 @@ public:
Engine();
~Engine() = default;
// 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; }

View file

@ -19,6 +19,8 @@
#include <QtCore/qjsonvalue.h>
#include <shared/JSONHelpers.h>
#include "SettingHandle.h"
#include "Context.h"
#include "gpu/Batch.h"
@ -59,10 +61,75 @@ class Job;
class Task;
class JobNoIO {};
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 (C::alwaysEnabled || C::enabled) {
_presets.insert(DEFAULT, _default);
}
if (!C::alwaysEnabled) {
_presets.insert(NONE, QVariantMap{{ "enabled", false }});
}
auto preset = _preset.get();
if (preset != _preset.getDefault() && _presets.contains(preset)) {
// Load the persisted configuration
C::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());
}
C::load(config);
}
}
protected:
QVariantMap _default;
QVariantMap _presets;
Setting::Handle<QString> _preset;
};
// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
class JobConfig : public QObject {
Q_OBJECT
public:
using Persistent = PersistentConfig<JobConfig>;
JobConfig() = default;
JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {}
@ -71,17 +138,31 @@ 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 {
Q_OBJECT
public:
using Persistent = PersistentConfig<TaskConfig>;
TaskConfig() = default ;
TaskConfig(bool enabled) : JobConfig(enabled) {}
@ -95,15 +176,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();
@ -236,8 +308,7 @@ public:
const Varying getInput() const { return _input; }
const Varying getOutput() const { return _output; }
Model(const Varying& input, Data data = Data()) : Concept(std::make_shared<C>()), _data(data), _input(input), _output(Output()) {
_config = _data._config; // use the data's config
Model(const Varying& input, Data data = Data()) : Concept(data._config), _data(data), _input(input), _output(Output()) {
std::static_pointer_cast<Config>(_config)->init(&_data);
applyConfiguration();
}
@ -273,9 +344,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

@ -54,6 +54,7 @@ public:
Spinner,
Checkbox,
Button,
ComboBox,
// Special casing for an unusual preference
Avatar
};
@ -236,6 +237,22 @@ protected:
QString _placeholderText;
};
class ComboBoxPreference : public EditPreference {
Q_OBJECT
Q_PROPERTY(QStringList items READ getItems CONSTANT)
public:
ComboBoxPreference(const QString& category, const QString& name, Getter getter, Setter setter)
: EditPreference(category, name, getter, setter) { }
Type getType() { return ComboBox; }
const QStringList& getItems() { return _items; }
void setItems(const QStringList& items) { _items = items; }
protected:
QStringList _items;
};
class BrowsePreference : public EditPreference {
Q_OBJECT
Q_PROPERTY(QString browseLabel READ getBrowseLabel CONSTANT)

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);
}