From 530e17a7e571adee8db6d9ec17cb12dda175efb1 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 22 Jul 2016 15:08:09 -0700 Subject: [PATCH 01/15] Move JSON parsing for custom dialog to be sooner --- libraries/ui/src/OffscreenUi.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 9e5f6c4e62..06ef456006 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -349,12 +349,8 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const } QVariant result = DependencyManager::get()->customInputDialog(icon, title, config); - if (result.isValid()) { - // We get a JSON encoded result, so we unpack it into a QVariant wrapping a QVariantMap - result = QVariant(QJsonDocument::fromJson(result.toString().toUtf8()).object().toVariantMap()); - if (ok) { - *ok = true; - } + if (ok && result.isValid()) { + *ok = true; } return result; @@ -386,7 +382,13 @@ QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, c return result; } - return waitForInputDialogResult(createCustomInputDialog(icon, title, config)); + QVariant result = waitForInputDialogResult(createCustomInputDialog(icon, title, config)); + if (result.isValid()) { + // We get a JSON encoded result, so we unpack it into a QVariant wrapping a QVariantMap + result = QVariant(QJsonDocument::fromJson(result.toString().toUtf8()).object().toVariantMap()); + } + + return result; } void OffscreenUi::togglePinned() { From c25b87c33ff213a915b73eceba30812ed4c6ea17 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 22 Jul 2016 15:15:11 -0700 Subject: [PATCH 02/15] Use custom dialog for asset server add to world --- interface/resources/qml/AssetServer.qml | 75 +++++++++++++++++-- .../entities/src/EntityScriptingInterface.cpp | 6 +- .../entities/src/EntityScriptingInterface.h | 3 +- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index c9b6305258..02c72f5b2d 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -141,18 +141,81 @@ ScrollingWindow { } function addToWorld() { - var url = assetProxyModel.data(treeView.selection.currentIndex, 0x103); + var defaultURL = assetProxyModel.data(treeView.selection.currentIndex, 0x103); - if (!url || !canAddToWorld(url)) { + if (!defaultURL || !canAddToWorld(defaultURL)) { return; } - var name = assetProxyModel.data(treeView.selection.currentIndex); + var SHAPE_TYPE_NONE = 0; + var SHAPE_TYPE_SIMPLE_HULL = 1; + var SHAPE_TYPE_SIMPLE_COMPOUND = 2; + var SHAPE_TYPE_STATIC_MESH = 3; - console.log("Asset browser - adding asset " + url + " (" + name + ") to world."); + var SHAPE_TYPES = []; + SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; + SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; + SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); - Entities.addModelEntity(name, url, addPosition); + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; + var DYNAMIC_DEFAULT = false; + var prompt = desktop.customInputDialog({ + textInput: { + label: "Model URL", + text: defaultURL + }, + comboBox: { + label: "Automatic Collisions", + index: SHAPE_TYPE_DEFAULT, + items: SHAPE_TYPES + }, + checkBox: { + label: "Dynamic", + checked: DYNAMIC_DEFAULT, + disableForItems: [ + SHAPE_TYPE_STATIC_MESH + ], + checkStateOnDisable: false, + warningOnDisable: "Models with automatic collisions set to 'Exact' cannot be dynamic" + } + }); + + prompt.selected.connect(function (jsonResult) { + if (jsonResult) { + var result = JSON.parse(jsonResult); + var url = result.textInput; + var shapeType; + switch (result.comboBox) { + case SHAPE_TYPE_SIMPLE_HULL: + shapeType = "simple-hull"; + break; + case SHAPE_TYPE_SIMPLE_COMPOUND: + shapeType = "simple-compound"; + break; + case SHAPE_TYPE_STATIC_MESH: + shapeType = "static-mesh"; + break; + default: + shapeType = "none"; + } + + var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; + if (shapeType === "static-mesh" && dynamic) { + // The prompt should prevent this case + print("Error: model cannot be both static mesh and dynamic. This should never happen."); + } else if (url) { + var name = assetProxyModel.data(treeView.selection.currentIndex); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); + var gravity = dynamic ? { x: 0, y: -10, z: 0 } : { x: 0, y: 0, z: 0 }; + + print("Asset browser - adding asset " + url + " (" + name + ") to world."); + + // Entities.addEntity doesn't work from QML, so we use this. + Entities.addModelEntity(name, url, shapeType, dynamic, addPosition, gravity); + } + } + }); } function copyURLToClipboard(index) { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 856e526b4c..653b37c3bb 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -198,12 +198,16 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } } -QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const glm::vec3& position) { +QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, + bool dynamic, const glm::vec3& position, const glm::vec3& gravity) { EntityItemProperties properties; properties.setType(EntityTypes::Model); properties.setName(name); properties.setModelURL(modelUrl); + properties.setShapeTypeFromString(shapeType); + properties.setDynamic(dynamic); properties.setPosition(position); + properties.setGravity(gravity); return addEntity(properties); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index e9024eb721..5aa0f5907e 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -86,7 +86,8 @@ public slots: Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine - Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const glm::vec3& position); + Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, + const glm::vec3& position, const glm::vec3& gravity); /// gets the current model properties for a specific model /// this function will not find return results in script engine contexts which don't have access to models From 344ec25bcd3ede709e5edc0553e01769bad75f32 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 25 Jul 2016 14:01:57 -0700 Subject: [PATCH 03/15] Fix gravity for dynamic entities --- interface/resources/qml/AssetServer.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 02c72f5b2d..3d0526e3c3 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -207,7 +207,14 @@ ScrollingWindow { } else if (url) { var name = assetProxyModel.data(treeView.selection.currentIndex); var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); - var gravity = dynamic ? { x: 0, y: -10, z: 0 } : { x: 0, y: 0, z: 0 }; + var gravity; + if (dynamic) { + // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a + // different scripting engine from QTScript. + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); + } else { + gravity = { x: 0, y: 0, z: 0 }; + } print("Asset browser - adding asset " + url + " (" + name + ") to world."); From 90154158b6eb1a5099fae6ece1b0b3cd5dbbc2f6 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 25 Jul 2016 14:08:07 -0700 Subject: [PATCH 04/15] Fix gravity zero vector --- interface/resources/qml/AssetServer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 3d0526e3c3..8d971e48d3 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -213,7 +213,7 @@ ScrollingWindow { // different scripting engine from QTScript. gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 10); } else { - gravity = { x: 0, y: 0, z: 0 }; + gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); } print("Asset browser - adding asset " + url + " (" + name + ") to world."); From 83dc9ea6bba4391b2642d9dba405f1afe8e0697f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 25 Jul 2016 21:46:30 -0700 Subject: [PATCH 05/15] punish slow scripts and don't send updates while physics is still loading --- interface/src/Application.cpp | 5 ++++ interface/src/Application.h | 1 + libraries/script-engine/src/ScriptEngine.cpp | 31 +++++++++++++++++--- libraries/script-engine/src/ScriptEngine.h | 5 ++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc37325ac5..faa1229cad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4678,6 +4678,11 @@ void Application::packetSent(quint64 length) { } void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { + + scriptEngine->setPhysicsEnabledFunction([this]() { + return isPhysicsEnabled(); + }); + // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. auto entityScriptingInterface = DependencyManager::get(); diff --git a/interface/src/Application.h b/interface/src/Application.h index a254072561..0af65f665f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -215,6 +215,7 @@ public: qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); } bool isAboutToQuit() const { return _aboutToQuit; } + bool isPhysicsEnabled() const { return _physicsEnabled; } // the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display // rendering of several elements depend on that diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9642aaf588..2ce3fcd7e0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -828,24 +828,42 @@ void ScriptEngine::run() { _lastUpdate = usecTimestampNow(); + qint64 totalSleepFor = 0; + std::chrono::microseconds totalUpdates; + auto lastLoopStart = clock::now(); + // TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine while (!_isFinished) { + auto thisLoopStart = clock::now(); + // Throttle to SCRIPT_FPS const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); + const std::chrono::microseconds MINIMUM_SLEEP { FRAME_DURATION / 2 }; + + auto beforeSleep = clock::now(); clock::time_point sleepTime(startTime + thisFrame++ * FRAME_DURATION); + auto wouldSleep = (sleepTime - clock::now()); + auto avgUpdates = totalUpdates / thisFrame; + + if (wouldSleep < avgUpdates) { + sleepTime = beforeSleep + avgUpdates; + } + std::this_thread::sleep_until(sleepTime); #ifdef SCRIPT_DELAY_DEBUG { - auto now = clock::now(); - uint64_t seconds = std::chrono::duration_cast(now - startTime).count(); + auto sleptTill = clock::now(); + uint64_t seconds = std::chrono::duration_cast(sleptTill - startTime).count(); if (seconds > 0) { // avoid division by zero and time travel uint64_t fps = thisFrame / seconds; // Overreporting artificially reduces the reported rate if (thisFrame % SCRIPT_FPS == 0) { qCDebug(scriptengine) << "Frame:" << thisFrame << - "Slept (us):" << std::chrono::duration_cast(now - sleepTime).count() << + "Slept (us):" << std::chrono::duration_cast(sleptTill - beforeSleep).count() << + "Avg Updates (us):" << avgUpdates.count() << + "Last loop time (us):" << std::chrono::duration_cast(thisLoopStart - lastLoopStart).count() << "FPS:" << fps; } } @@ -874,16 +892,21 @@ void ScriptEngine::run() { qint64 now = usecTimestampNow(); // we check for 'now' in the past in case people set their clock back - if (_lastUpdate < now) { + if (_isPhysicsEnabledFunc() && _lastUpdate < now) { float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND; if (!_isFinished) { + auto preUpdate = clock::now(); emit update(deltaTime); + auto postUpdate = clock::now(); + auto elapsed = (postUpdate - preUpdate); + totalUpdates += std::chrono::duration_cast(elapsed); } } _lastUpdate = now; // Debug and clear exceptions hadUncaughtExceptions(*this, _fileNameString); + lastLoopStart = thisLoopStart; } qCDebug(scriptengine) << "Script Engine stopping:" << getFilename(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1077dce686..a62f6e1a88 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -168,6 +168,8 @@ public: // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + void setPhysicsEnabledFunction(std::function func) { _isPhysicsEnabledFunc = func; } + public slots: void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); void updateMemoryCost(const qint64&); @@ -236,6 +238,9 @@ protected: QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty. void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation); void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); + + std::function _isPhysicsEnabledFunc{ [](){ return true; } }; + }; #endif // hifi_ScriptEngine_h From a12034cb45255d19fc1b880a2e8e4beaff8e7b9e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 26 Jul 2016 07:54:55 -0700 Subject: [PATCH 06/15] fix unix warning, added comments --- libraries/script-engine/src/ScriptEngine.cpp | 35 ++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2ce3fcd7e0..fd09e9a018 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -828,42 +828,44 @@ void ScriptEngine::run() { _lastUpdate = usecTimestampNow(); - qint64 totalSleepFor = 0; std::chrono::microseconds totalUpdates; - auto lastLoopStart = clock::now(); // TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine while (!_isFinished) { - auto thisLoopStart = clock::now(); + auto beforeSleep = clock::now(); // Throttle to SCRIPT_FPS + // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will + // calculate a sleepUntil to be the time from our start time until the original target + // sleepUntil for this frame. const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); - const std::chrono::microseconds MINIMUM_SLEEP { FRAME_DURATION / 2 }; + clock::time_point sleepUntil(startTime + thisFrame++ * FRAME_DURATION); - auto beforeSleep = clock::now(); - clock::time_point sleepTime(startTime + thisFrame++ * FRAME_DURATION); - auto wouldSleep = (sleepTime - clock::now()); - auto avgUpdates = totalUpdates / thisFrame; + // However, if our sleepUntil is not at least our average update time into the future + // it means our script is taking too long in it's updates, and we want to punish the + // script a little bit. So we will force the sleepUntil to be at least our averageUpdate + // time into the future. + auto wouldSleep = (sleepUntil - clock::now()); + auto avgerageUpdate = totalUpdates / thisFrame; - if (wouldSleep < avgUpdates) { - sleepTime = beforeSleep + avgUpdates; + if (wouldSleep < avgerageUpdate) { + sleepUntil = beforeSleep + avgerageUpdate; } - std::this_thread::sleep_until(sleepTime); + std::this_thread::sleep_until(sleepUntil); #ifdef SCRIPT_DELAY_DEBUG { - auto sleptTill = clock::now(); - uint64_t seconds = std::chrono::duration_cast(sleptTill - startTime).count(); + auto actuallySleptUntil = clock::now(); + uint64_t seconds = std::chrono::duration_cast(actuallySleptUntil - startTime).count(); if (seconds > 0) { // avoid division by zero and time travel uint64_t fps = thisFrame / seconds; // Overreporting artificially reduces the reported rate if (thisFrame % SCRIPT_FPS == 0) { qCDebug(scriptengine) << "Frame:" << thisFrame << - "Slept (us):" << std::chrono::duration_cast(sleptTill - beforeSleep).count() << - "Avg Updates (us):" << avgUpdates.count() << - "Last loop time (us):" << std::chrono::duration_cast(thisLoopStart - lastLoopStart).count() << + "Slept (us):" << std::chrono::duration_cast(actuallySleptUntil - beforeSleep).count() << + "Avg Updates (us):" << avgerageUpdate.count() << "FPS:" << fps; } } @@ -906,7 +908,6 @@ void ScriptEngine::run() { // Debug and clear exceptions hadUncaughtExceptions(*this, _fileNameString); - lastLoopStart = thisLoopStart; } qCDebug(scriptengine) << "Script Engine stopping:" << getFilename(); From 2d199fe3d007f3a6fb7f9c1099aac08c583e416d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 26 Jul 2016 08:08:32 -0700 Subject: [PATCH 07/15] rename function for better clarity --- interface/src/Application.cpp | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- libraries/script-engine/src/ScriptEngine.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index faa1229cad..eba400ebc5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4679,7 +4679,7 @@ void Application::packetSent(quint64 length) { void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { - scriptEngine->setPhysicsEnabledFunction([this]() { + scriptEngine->setEmitScriptUpdatesFunction([this]() { return isPhysicsEnabled(); }); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fd09e9a018..21eae99307 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -894,7 +894,7 @@ void ScriptEngine::run() { qint64 now = usecTimestampNow(); // we check for 'now' in the past in case people set their clock back - if (_isPhysicsEnabledFunc() && _lastUpdate < now) { + if (_emitScriptUpdates() && _lastUpdate < now) { float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND; if (!_isFinished) { auto preUpdate = clock::now(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index a62f6e1a88..e093f0393b 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -168,7 +168,7 @@ public: // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } - void setPhysicsEnabledFunction(std::function func) { _isPhysicsEnabledFunc = func; } + void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } public slots: void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); @@ -239,7 +239,7 @@ protected: void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function operation); void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); - std::function _isPhysicsEnabledFunc{ [](){ return true; } }; + std::function _emitScriptUpdates{ [](){ return true; } }; }; From 3962487ee394fb278b3f7fed20a41032a5c05250 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 08:51:12 -0700 Subject: [PATCH 08/15] Fix depth state caching / resetting --- .../gpu-gl/src/gpu/gl/GLBackendState.cpp | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index a42b0dca6f..f04a0a75f8 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -158,64 +158,73 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) { } void GLBackend::do_setStateDepthTest(State::DepthTest test) { - if (_pipeline._stateCache.depthTest != test) { + const auto& current = _pipeline._stateCache.depthTest; + if (current != test) { if (test.isEnabled()) { glEnable(GL_DEPTH_TEST); - glDepthMask(test.getWriteMask()); - glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); } else { glDisable(GL_DEPTH_TEST); } + if (test.getWriteMask() != current.getWriteMask()) { + glDepthMask(test.getWriteMask()); + } + if (test.getFunction() != current.getFunction()) { + glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); + } if (CHECK_GL_ERROR()) { qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") << "Func=" << test.getFunction() << "Raw=" << test.getRaw(); } - _pipeline._stateCache.depthTest = test; } } void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) { - - if ((_pipeline._stateCache.stencilActivation != activation) - || (_pipeline._stateCache.stencilTestFront != frontTest) - || (_pipeline._stateCache.stencilTestBack != backTest)) { + const auto& currentActivation = _pipeline._stateCache.stencilActivation; + const auto& currentTestFront = _pipeline._stateCache.stencilTestFront; + const auto& currentTestBack = _pipeline._stateCache.stencilTestBack; + if ((currentActivation != activation) + || (currentTestFront != frontTest) + || (currentTestBack != backTest)) { if (activation.isEnabled()) { glEnable(GL_STENCIL_TEST); - - if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { - glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); - glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); - } else { - glStencilMask(activation.getWriteMaskFront()); - } - - static GLenum STENCIL_OPS[] = { - GL_KEEP, - GL_ZERO, - GL_REPLACE, - GL_INCR_WRAP, - GL_DECR_WRAP, - GL_INVERT, - GL_INCR, - GL_DECR }; - - if (frontTest != backTest) { - glStencilOpSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); - glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); - - glStencilOpSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]); - glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[backTest.getFunction()], backTest.getReference(), backTest.getReadMask()); - } else { - glStencilOp(STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); - glStencilFunc(COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); - } - } else { + } + else { glDisable(GL_STENCIL_TEST); } + + if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { + glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); + glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); + } + else { + glStencilMask(activation.getWriteMaskFront()); + } + + static GLenum STENCIL_OPS[State::NUM_STENCIL_OPS] = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR_WRAP, + GL_DECR_WRAP, + GL_INVERT, + GL_INCR, + GL_DECR }; + + if (frontTest != backTest) { + glStencilOpSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); + glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); + + glStencilOpSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]); + glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[backTest.getFunction()], backTest.getReference(), backTest.getReadMask()); + } else { + glStencilOp(STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); + glStencilFunc(COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); + } + (void)CHECK_GL_ERROR(); _pipeline._stateCache.stencilActivation = activation; From ac743b6acbe20dda7fde4c630a00f1ad2f5cebad Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 11:53:37 -0700 Subject: [PATCH 09/15] PR feedback --- .../gpu-gl/src/gpu/gl/GLBackendState.cpp | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index f04a0a75f8..b1e4f427db 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -181,26 +181,24 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) { } } -void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) { +void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest testFront, State::StencilTest testBack) { const auto& currentActivation = _pipeline._stateCache.stencilActivation; const auto& currentTestFront = _pipeline._stateCache.stencilTestFront; const auto& currentTestBack = _pipeline._stateCache.stencilTestBack; if ((currentActivation != activation) - || (currentTestFront != frontTest) - || (currentTestBack != backTest)) { + || (currentTestFront != testFront) + || (currentTestBack != testBack)) { if (activation.isEnabled()) { glEnable(GL_STENCIL_TEST); - } - else { + } else { glDisable(GL_STENCIL_TEST); } if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); - } - else { + } else { glStencilMask(activation.getWriteMaskFront()); } @@ -214,22 +212,22 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S GL_INCR, GL_DECR }; - if (frontTest != backTest) { - glStencilOpSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); - glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); + if (testFront != testBack) { + glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getPassOp()], STENCIL_OPS[testFront.getDepthFailOp()]); + glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); - glStencilOpSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]); - glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[backTest.getFunction()], backTest.getReference(), backTest.getReadMask()); + glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getPassOp()], STENCIL_OPS[testBack.getDepthFailOp()]); + glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[testBack.getFunction()], testBack.getReference(), testBack.getReadMask()); } else { - glStencilOp(STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]); - glStencilFunc(COMPARISON_TO_GL[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask()); + glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getPassOp()], STENCIL_OPS[testFront.getDepthFailOp()]); + glStencilFunc(COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); } (void)CHECK_GL_ERROR(); _pipeline._stateCache.stencilActivation = activation; - _pipeline._stateCache.stencilTestFront = frontTest; - _pipeline._stateCache.stencilTestBack = backTest; + _pipeline._stateCache.stencilTestFront = testFront; + _pipeline._stateCache.stencilTestBack = testBack; } } From a455f3a435961b8d2024c8d31dddbe6c9e6aa12c Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 15:25:36 -0700 Subject: [PATCH 10/15] Add frame concept to gpu library --- libraries/gpu/src/gpu/Forward.h | 4 ++++ libraries/gpu/src/gpu/Frame.cpp | 10 ++++++++++ libraries/gpu/src/gpu/Frame.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 libraries/gpu/src/gpu/Frame.cpp create mode 100644 libraries/gpu/src/gpu/Frame.h diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 7fe6739ff7..e2a4ad38dd 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -21,6 +22,9 @@ namespace gpu { class Context; using ContextPointer = std::shared_ptr; class GPUObject; + class Frame; + using FramePointer = std::shared_ptr; + using FrameHandler = std::function; using Stamp = int; using uint32 = uint32_t; diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp new file mode 100644 index 0000000000..d36c7f5537 --- /dev/null +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -0,0 +1,10 @@ +// +// Created by Bradley Austin Davis on 2016/07/26 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "Frame.h" + +using namespace gpu; diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h new file mode 100644 index 0000000000..99296cc91e --- /dev/null +++ b/libraries/gpu/src/gpu/Frame.h @@ -0,0 +1,28 @@ +// +// Created by Bradley Austin Davis on 2016/07/26 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Frame_h +#define hifi_gpu_Frame_h + +#include "Forward.h" + +namespace gpu { + +class Frame { +public: + /// The sensor pose used for rendering the frame, only applicable for HMDs + glm::mat4 pose; + /// The collection of batches which make up the frame + std::vector batches; + /// The destination framebuffer in which the results will be placed + FramebufferPointer framebuffer; +}; + +}; + + +#endif From bb6abf11d33b2f4f29da312a912d25c28323abd0 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 15:32:31 -0700 Subject: [PATCH 11/15] FramebufferCache, cleanup & thread safety --- .../render-utils/src/FramebufferCache.cpp | 27 +++++++------------ libraries/render-utils/src/FramebufferCache.h | 13 ++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 5375de273a..4ada0ce980 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -11,34 +11,25 @@ #include "FramebufferCache.h" -#include - #include +#include +#include -#include -#include -#include #include "RenderUtilsLogging.h" -static QQueue _cachedFramebuffers; - -FramebufferCache::FramebufferCache() { -} - -FramebufferCache::~FramebufferCache() { - _cachedFramebuffers.clear(); -} - void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; _selfieFramebuffer.reset(); - _cachedFramebuffers.clear(); _occlusionFramebuffer.reset(); _occlusionTexture.reset(); _occlusionBlurredFramebuffer.reset(); _occlusionBlurredTexture.reset(); + { + std::unique_lock lock(_mutex); + _cachedFramebuffers.clear(); + } } } @@ -55,8 +46,6 @@ void FramebufferCache::createPrimaryFramebuffer() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - - resizeAmbientOcclusionBuffers(); } @@ -87,7 +76,8 @@ void FramebufferCache::resizeAmbientOcclusionBuffers() { gpu::FramebufferPointer FramebufferCache::getFramebuffer() { - if (_cachedFramebuffers.isEmpty()) { + std::unique_lock lock(_mutex); + if (_cachedFramebuffers.empty()) { _cachedFramebuffers.push_back(gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_SRGBA_32, _frameBufferSize.width(), _frameBufferSize.height()))); } gpu::FramebufferPointer result = _cachedFramebuffers.front(); @@ -96,6 +86,7 @@ gpu::FramebufferPointer FramebufferCache::getFramebuffer() { } void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebuffer) { + std::unique_lock lock(_mutex); if (QSize(framebuffer->getSize().x, framebuffer->getSize().y) == _frameBufferSize) { _cachedFramebuffers.push_back(framebuffer); } diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index d3d26c35b0..10ec664669 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -11,13 +11,10 @@ #include -#include +#include +#include #include -namespace gpu { -class Batch; -} - /// Stores cached textures, including render-to-texture targets. class FramebufferCache : public Dependency { SINGLETON_DEPENDENCY @@ -47,9 +44,6 @@ public: void releaseFramebuffer(const gpu::FramebufferPointer& framebuffer); private: - FramebufferCache(); - virtual ~FramebufferCache(); - void createPrimaryFramebuffer(); gpu::FramebufferPointer _shadowFramebuffer; @@ -65,6 +59,9 @@ private: QSize _frameBufferSize{ 100, 100 }; int _AOResolutionLevel = 1; // AO perform at half res + std::mutex _mutex; + std::list _cachedFramebuffers; + // Resize/reallocate the buffers used for AO // the size of the AO buffers is scaled by the AOResolutionScale; void resizeAmbientOcclusionBuffers(); From fa2089828564e9930982ddad67bd19ff3e3e7aab Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 16:13:22 -0700 Subject: [PATCH 12/15] Remove last direct use of gpu::Context::render() --- libraries/render-utils/src/RenderDeferredTask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bb7adf3f80..e39d659051 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -347,10 +347,10 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning if (_opaquePass) { - gpu::Batch batch; - batch.enableStereo(false); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); - args->_context->render(batch); + gpu::doInBatch(args->_context, [&](gpu::Batch& batch){ + batch.enableStereo(false); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); + }); } // Render the items From a9740b803fa91931e845615a8fbf5efad2c5f54d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 16:13:48 -0700 Subject: [PATCH 13/15] Defer batch execution to the end of the frame generation --- interface/src/Application.cpp | 31 +++++++----- libraries/gpu/src/gpu/Batch.cpp | 27 +++++++++++ libraries/gpu/src/gpu/Context.cpp | 80 ++++++++++++++++++++++++------- libraries/gpu/src/gpu/Context.h | 73 +++++++++++----------------- libraries/gpu/src/gpu/Forward.h | 9 ++++ libraries/gpu/src/gpu/Frame.h | 1 + 6 files changed, 147 insertions(+), 74 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eba400ebc5..e294ae38ad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1689,6 +1689,17 @@ void Application::paintGL() { renderArgs._context->syncCache(); } + auto framebufferCache = DependencyManager::get(); + // Final framebuffer that will be handled to the display-plugin + auto finalFramebuffer = framebufferCache->getFramebuffer(); + + _gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose()); + // Reset the gpu::Context Stages + // Back to the default framebuffer; + gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); + auto inputs = AvatarInputs::getInstance(); if (inputs->mirrorVisible()) { PerformanceTimer perfTimer("Mirror"); @@ -1711,10 +1722,6 @@ void Application::paintGL() { QSize size = getDeviceSize(); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); _applicationOverlay.renderOverlay(&renderArgs); - auto overlayTexture = _applicationOverlay.acquireOverlay(); - if (overlayTexture) { - displayPlugin->submitOverlayTexture(overlayTexture); - } } glm::vec3 boomOffset; @@ -1816,12 +1823,8 @@ void Application::paintGL() { getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); // Primary rendering pass - auto framebufferCache = DependencyManager::get(); const QSize size = framebufferCache->getFrameBufferSize(); - // Final framebuffer that will be handled to the display-plugin - auto finalFramebuffer = framebufferCache->getFramebuffer(); - { PROFILE_RANGE(__FUNCTION__ "/mainRender"); PerformanceTimer perfTimer("mainRender"); @@ -1880,6 +1883,13 @@ void Application::paintGL() { renderArgs._context->enableStereo(false); } + _gpuContext->endFrame(); + + gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay(); + if (overlayTexture) { + displayPlugin->submitOverlayTexture(overlayTexture); + } + // deliver final composited scene to the display plugin { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); @@ -1900,11 +1910,6 @@ void Application::paintGL() { { Stats::getInstance()->setRenderDetails(renderArgs._details); - // Reset the gpu::Context Stages - // Back to the default framebuffer; - gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { - batch.resetStages(); - }); } uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 9161ee3642..ab1337070c 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -46,6 +46,33 @@ Batch::Batch() { _drawCallInfos.reserve(_drawCallInfosMax); } +Batch::Batch(const Batch& batch_) { + Batch& batch = *const_cast(&batch_); + _commands.swap(batch._commands); + _commandOffsets.swap(batch._commandOffsets); + _params.swap(batch._params); + _data.swap(batch._data); + _invalidModel = batch._invalidModel; + _currentModel = batch._currentModel; + _objects.swap(batch._objects); + _currentNamedCall = batch._currentNamedCall; + + _buffers._items.swap(batch._buffers._items); + _textures._items.swap(batch._textures._items); + _streamFormats._items.swap(batch._streamFormats._items); + _transforms._items.swap(batch._transforms._items); + _pipelines._items.swap(batch._pipelines._items); + _framebuffers._items.swap(batch._framebuffers._items); + _drawCallInfos.swap(batch._drawCallInfos); + _queries._items.swap(batch._queries._items); + _lambdas._items.swap(batch._lambdas._items); + _profileRanges._items.swap(batch._profileRanges._items); + _names._items.swap(batch._names._items); + _namedData.swap(batch._namedData); + _enableStereo = batch._enableStereo; + _enableSkybox = batch._enableSkybox; +} + Batch::~Batch() { _commandsMax = std::max(_commands.size(), _commandsMax); _commandOffsetsMax = std::max(_commandOffsets.size(), _commandOffsetsMax); diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 2c27260331..ff43491133 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Context.h" - +#include "Frame.h" using namespace gpu; Context::CreateBackend Context::_createBackendCallback = nullptr; @@ -20,6 +20,13 @@ Context::Context() { if (_createBackendCallback) { _backend.reset(_createBackendCallback()); } + + _frameHandler = [this](Frame& frame){ + for (size_t i = 0; i < frame.batches.size(); ++i) { + _backend->_stereo = frame.stereoStates[i]; + _backend->render(frame.batches[i]); + } + }; } Context::Context(const Context& context) { @@ -28,6 +35,43 @@ Context::Context(const Context& context) { Context::~Context() { } +void Context::setFrameHandler(FrameHandler handler) { + _frameHandler = handler; +} + +#define DEFERRED_RENDERING + +void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) { + _currentFrame = Frame(); + _currentFrame.framebuffer = outputFramebuffer; + _currentFrame.pose = renderPose; + _frameActive = true; +} + +void Context::append(Batch& batch) { + if (!_frameActive) { + qWarning() << "Batch executed outside of frame boundaries"; + } +#ifdef DEFERRED_RENDERING + _currentFrame.batches.emplace_back(batch); + _currentFrame.stereoStates.emplace_back(_stereo); +#else + _backend->_stereo = _stereo; + _backend->render(batch); +#endif +} + +void Context::endFrame() { +#ifdef DEFERRED_RENDERING + if (_frameHandler) { + _frameHandler(_currentFrame); + } +#endif + _currentFrame = Frame(); + _frameActive = false; +} + + bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { if (shader.isProgram() && _makeProgramCallback) { return _makeProgramCallback(shader, bindings); @@ -35,36 +79,38 @@ bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { return false; } -void Context::render(Batch& batch) { - PROFILE_RANGE(__FUNCTION__); - _backend->render(batch); -} - void Context::enableStereo(bool enable) { - _backend->enableStereo(enable); + _stereo._enable = enable; } bool Context::isStereo() { - return _backend->isStereo(); + return _stereo._enable; } void Context::setStereoProjections(const mat4 eyeProjections[2]) { - _backend->setStereoProjections(eyeProjections); + for (int i = 0; i < 2; ++i) { + _stereo._eyeProjections[i] = eyeProjections[i]; + } } -void Context::setStereoViews(const mat4 eyeViews[2]) { - _backend->setStereoViews(eyeViews); +void Context::setStereoViews(const mat4 views[2]) { + for (int i = 0; i < 2; ++i) { + _stereo._eyeViews[i] = views[i]; + } } void Context::getStereoProjections(mat4* eyeProjections) const { - _backend->getStereoProjections(eyeProjections); + for (int i = 0; i < 2; ++i) { + eyeProjections[i] = _stereo._eyeProjections[i]; + } } void Context::getStereoViews(mat4* eyeViews) const { - _backend->getStereoViews(eyeViews); + for (int i = 0; i < 2; ++i) { + eyeViews[i] = _stereo._eyeViews[i]; + } } - void Context::syncCache() { PROFILE_RANGE(__FUNCTION__); _backend->syncCache(); @@ -103,12 +149,12 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S if (!_stereo._skybox) { offsetTransform.postTranslate(-Vec3(_stereo._eyeViews[eye][3])); } else { - // FIXME: If "skybox" the ipd is set to 0 for now, let s try to propose a better solution for this in the future + // FIXME: If "skybox" the ipd is set to 0 for now, let s try to propose a better solution for this in the future } result._projection = _stereo._eyeProjections[eye]; result.recomputeDerived(offsetTransform); - result._stereoInfo = Vec4(1.0f, (float) eye, 0.0f, 0.0f); + result._stereoInfo = Vec4(1.0f, (float)eye, 0.0f, 0.0f); return result; } @@ -125,7 +171,7 @@ std::atomic Context::_textureGPUTransferCount{ 0 }; void Context::incrementBufferGPUCount() { _bufferGPUCount++; } -void Context::decrementBufferGPUCount() { +void Context::decrementBufferGPUCount() { _bufferGPUCount--; } void Context::updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) { diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 2d0baa0497..5f894318f2 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -16,12 +16,13 @@ #include +#include "Forward.h" #include "Batch.h" - #include "Resource.h" #include "Texture.h" #include "Pipeline.h" #include "Framebuffer.h" +#include "Frame.h" class QImage; @@ -46,51 +47,11 @@ public: ContextStats(const ContextStats& stats) = default; }; -struct StereoState { - bool _enable{ false }; - bool _skybox{ false }; - // 0 for left eye, 1 for right eye - uint8_t _pass{ 0 }; - mat4 _eyeViews[2]; - mat4 _eyeProjections[2]; -}; - class Backend { public: virtual~ Backend() {}; virtual void render(Batch& batch) = 0; - virtual void enableStereo(bool enable) { - _stereo._enable = enable; - } - - virtual bool isStereo() { - return _stereo._enable; - } - - void setStereoProjections(const mat4 eyeProjections[2]) { - for (int i = 0; i < 2; ++i) { - _stereo._eyeProjections[i] = eyeProjections[i]; - } - } - - void setStereoViews(const mat4 views[2]) { - for (int i = 0; i < 2; ++i) { - _stereo._eyeViews[i] = views[i]; - } - } - - void getStereoProjections(mat4* eyeProjections) const { - for (int i = 0; i < 2; ++i) { - eyeProjections[i] = _stereo._eyeProjections[i]; - } - } - - void getStereoViews(mat4* eyeViews) const { - for (int i = 0; i < 2; ++i) { - eyeViews[i] = _stereo._eyeViews[i]; - } - } virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; @@ -137,8 +98,25 @@ public: static void decrementTextureGPUTransferCount(); protected: - StereoState _stereo; + virtual bool isStereo() { + return _stereo._enable; + } + + void getStereoProjections(mat4* eyeProjections) const { + for (int i = 0; i < 2; ++i) { + eyeProjections[i] = _stereo._eyeProjections[i]; + } + } + + void getStereoViews(mat4* eyeViews) const { + for (int i = 0; i < 2; ++i) { + eyeViews[i] = _stereo._eyeViews[i]; + } + } + + friend class Context; ContextStats _stats; + StereoState _stereo; }; class Context { @@ -161,7 +139,10 @@ public: Context(); ~Context(); - void render(Batch& batch); + void setFrameHandler(FrameHandler handler); + void beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose = glm::mat4()); + void append(Batch& batch); + void endFrame(); void enableStereo(bool enable = true); bool isStereo(); @@ -191,6 +172,10 @@ protected: Context(const Context& context); std::unique_ptr _backend; + bool _frameActive { false }; + Frame _currentFrame; + FrameHandler _frameHandler; + StereoState _stereo; // This function can only be called by "static Shader::makeProgram()" // makeProgramShader(...) make a program shader ready to be used in a Batch. @@ -234,7 +219,7 @@ template void doInBatch(std::shared_ptr context, F f) { gpu::Batch batch; f(batch); - context->render(batch); + context->append(batch); } }; diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index e2a4ad38dd..3b04b17d87 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -86,6 +86,15 @@ namespace gpu { class TextureView; using TextureViews = std::vector; + struct StereoState { + bool _enable{ false }; + bool _skybox{ false }; + // 0 for left eye, 1 for right eye + uint8 _pass{ 0 }; + Mat4 _eyeViews[2]; + Mat4 _eyeProjections[2]; + }; + namespace gl { class GLBuffer; } diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 99296cc91e..ed5e2ea179 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -18,6 +18,7 @@ public: glm::mat4 pose; /// The collection of batches which make up the frame std::vector batches; + std::vector stereoStates; /// The destination framebuffer in which the results will be placed FramebufferPointer framebuffer; }; From 54a7265be8ee66b3a4f0a849ccb70c9ef7fb8edd Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 16:32:56 -0700 Subject: [PATCH 14/15] Removing empty file, because jenkins --- libraries/gpu/src/gpu/Frame.cpp | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 libraries/gpu/src/gpu/Frame.cpp diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp deleted file mode 100644 index d36c7f5537..0000000000 --- a/libraries/gpu/src/gpu/Frame.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/26 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "Frame.h" - -using namespace gpu; From 5bbcbbb811985e1bfba7bd054f95ec72f82f1739 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 26 Jul 2016 17:06:44 -0700 Subject: [PATCH 15/15] Update render perf tool to use the new API --- tests/render-perf/src/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ace637e92a..22dd96b83b 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -403,7 +403,12 @@ private: renderArgs._blitFramebuffer = finalFramebuffer; } + _gpuContext->beginFrame(renderArgs._blitFramebuffer); + gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { + batch.resetStages(); + }); render(&renderArgs); + _gpuContext->endFrame(); GLuint glTex; { auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0); @@ -428,9 +433,6 @@ private: _offscreenContext->makeCurrent(); framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer); renderArgs._blitFramebuffer.reset(); - gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { - batch.resetStages(); - }); _fpsCounter.increment(); static size_t _frameCount { 0 }; ++_frameCount;