diff --git a/examples/tests/playaPerformanceTest.js b/examples/tests/playaPerformanceTest.js new file mode 100644 index 0000000000..4c8a728a15 --- /dev/null +++ b/examples/tests/playaPerformanceTest.js @@ -0,0 +1,11 @@ +var qml = Script.resolvePath('playaPerformanceTest.qml'); +qmlWindow = new OverlayWindow({ + title: 'Test Qml', + source: qml, + height: 320, + width: 640, + toolWindow: false, + visible: true +}); + + diff --git a/examples/tests/playaPerformanceTest.qml b/examples/tests/playaPerformanceTest.qml new file mode 100644 index 0000000000..f1382358ae --- /dev/null +++ b/examples/tests/playaPerformanceTest.qml @@ -0,0 +1,193 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Rectangle { + id: root + width: parent ? parent.width : 100 + height: parent ? parent.height : 100 + + signal sendToScript(var message); + property var values: []; + property var host: AddressManager.hostname + + + Component.onCompleted: { + Window.domainChanged.connect(function(newDomain){ + if (newDomain !== root.host) { + root.host = AddressManager.hostname; + } + }); + } + + onHostChanged: { + if (root.running) { + if (host !== "Dreaming" && host !== "Playa") { + return; + } + + console.log("PERF new domain " + host) + if (host === "Dreaming") { + AddressManager.handleLookupString("Playa"); + return; + } + + if (host === "Playa") { + console.log("PERF starting timers and frame timing"); + // If we've arrived, start running the test + FrameTimings.start(); + rotationTimer.start(); + stopTimer.start(); + } + } + } + + function startTest() { + console.log("PERF startTest()"); + root.running = true + console.log("PERF current host: " + AddressManager.hostname) + // If we're already in playa, we need to go somewhere else... + if ("Playa" === AddressManager.hostname) { + console.log("PERF Navigating to dreaming") + AddressManager.handleLookupString("Dreaming/0,0,0"); + } else { + console.log("PERF Navigating to playa") + AddressManager.handleLookupString("Playa"); + } + } + + function stopTest() { + console.log("PERF stopTest()"); + root.running = false; + stopTimer.stop(); + rotationTimer.stop(); + FrameTimings.finish(); + root.values = FrameTimings.getValues(); + AddressManager.handleLookupString("Dreaming/0,0,0"); + resultGraph.requestPaint(); + console.log("PERF Value Count: " + root.values.length); + console.log("PERF Max: " + FrameTimings.max); + console.log("PERF Min: " + FrameTimings.min); + console.log("PERF Avg: " + FrameTimings.mean); + console.log("PERF StdDev: " + FrameTimings.standardDeviation); + } + + function yaw(a) { + var y = -Math.sin( a / 2.0 ); + var w = Math.cos( a / 2.0 ); + var l = Math.sqrt((y * y) + (w * w)); + return Qt.quaternion(w / l, 0, y / l, 0); + } + + function rotate() { + MyAvatar.setOrientationVar(yaw(Date.now() / 1000)); + } + + property bool running: false + + Timer { + id: stopTimer + interval: 30 * 1000 + repeat: false + running: false + onTriggered: stopTest(); + } + + Timer { + id: rotationTimer + interval: 100 + repeat: true + running: false + onTriggered: rotate(); + } + + Row { + id: row + anchors { left: parent.left; right: parent.right; } + spacing: 8 + Button { + text: root.running ? "Stop" : "Run" + onClicked: root.running ? stopTest() : startTest(); + } + } + +// Rectangle { +// anchors { left: parent.left; right: parent.right; top: row.bottom; topMargin: 8; bottom: parent.bottom; } +// //anchors.fill: parent +// color: "#7fff0000" +// } + + // Return the maximum value from a set of values + function vv(i, max) { + var perValue = values.length / max; + var start = Math.floor(perValue * i); + var end = Math.min(values.length, Math.floor(start + perValue)); + var result = 0; + for (var j = start; j <= end; ++j) { + result = Math.max(result, values[j]); + } + return result; + } + + Canvas { + id: resultGraph + anchors { left: parent.left; right: parent.right; top: row.bottom; margins: 16; bottom: parent.bottom; } + property real maxValue: 200; + property real perFrame: 10000; + property real k1: (5 / maxValue) * height; + property real k2: (10 / maxValue) * height; + property real k3: (100 / maxValue) * height; + + onPaint: { + var ctx = getContext("2d"); + if (values.length === 0) { + ctx.fillStyle = Qt.rgba(1, 0, 0, 1); + ctx.fillRect(0, 0, width, height); + return; + } + + + //ctx.setTransform(1, 0, 0, -1, 0, 0); + ctx.fillStyle = Qt.rgba(0, 0, 0, 1); + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle= "gray"; + ctx.lineWidth="1"; + ctx.beginPath(); + for (var i = 0; i < width; ++i) { + var value = vv(i, width); //values[Math.min(i, values.length - 1)]; + value /= 10000; + value /= maxValue; + ctx.moveTo(i, height); + ctx.lineTo(i, height - (height * value)); + } + ctx.stroke(); + + ctx.strokeStyle= "green"; + ctx.lineWidth="2"; + ctx.beginPath(); + var lineHeight = height - k1; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + ctx.strokeStyle= "yellow"; + ctx.lineWidth="2"; + ctx.beginPath(); + lineHeight = height - k2; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + ctx.strokeStyle= "red"; + ctx.lineWidth="2"; + ctx.beginPath(); + lineHeight = height - k3; + ctx.moveTo(0, lineHeight); + ctx.lineTo(width, lineHeight); + ctx.stroke(); + + } + } +} + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3a0b0998c5..f8bfd8f3d4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -147,6 +147,8 @@ #include "Util.h" #include "InterfaceParentFinder.h" +#include "FrameTimingsScriptingInterface.h" + // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -1334,6 +1336,8 @@ void Application::initializeGL() { InfoView::show(INFO_HELP_PATH, true); } +FrameTimingsScriptingInterface _frameTimingsScriptingInterface; + extern void setupPreferences(); void Application::initializeUi() { @@ -1378,6 +1382,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Messages", DependencyManager::get().data()); rootContext->setContextProperty("Recording", DependencyManager::get().data()); rootContext->setContextProperty("Preferences", DependencyManager::get().data()); + rootContext->setContextProperty("AddressManager", DependencyManager::get().data()); + rootContext->setContextProperty("FrameTimings", &_frameTimingsScriptingInterface); rootContext->setContextProperty("TREE_SCALE", TREE_SCALE); rootContext->setContextProperty("Quat", new Quat()); @@ -1421,6 +1427,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { @@ -1463,9 +1470,9 @@ void Application::initializeUi() { }); } + void Application::paintGL() { updateHeartbeat(); - // Some plugins process message events, potentially leading to // re-entering a paint event. don't allow further processing if this // happens @@ -1483,6 +1490,7 @@ void Application::paintGL() { _frameCount++; _frameCounter.increment(); + auto lastPaintBegin = usecTimestampNow(); PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount); PerformanceTimer perfTimer("paintGL"); @@ -1735,6 +1743,9 @@ void Application::paintGL() { batch.resetStages(); }); } + + uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; + _frameTimingsScriptingInterface.addValue(lastPaintDuration); } void Application::runTests() { diff --git a/interface/src/FrameTimingsScriptingInterface.cpp b/interface/src/FrameTimingsScriptingInterface.cpp new file mode 100644 index 0000000000..6c8c164804 --- /dev/null +++ b/interface/src/FrameTimingsScriptingInterface.cpp @@ -0,0 +1,53 @@ +// +// Created by Bradley Austin Davis on 2016/04/04 +// 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 "FrameTimingsScriptingInterface.h" + +#include + +void FrameTimingsScriptingInterface::start() { + _values.clear(); + DependencyManager::get()->setUnusedResourceCacheSize(0); + _values.reserve(8192); + _active = true; +} + +void FrameTimingsScriptingInterface::addValue(uint64_t value) { + if (_active) { + _values.push_back(value); + } +} + +void FrameTimingsScriptingInterface::finish() { + _active = false; + uint64_t total = 0; + _min = std::numeric_limits::max(); + _max = std::numeric_limits::lowest(); + size_t count = _values.size(); + for (auto i = 0; i < count; ++i) { + const uint64_t& value = _values[i]; + _max = std::max(_max, value); + _min = std::min(_min, value); + total += value; + } + _mean = (float)total / (float)count; + float deviationTotal = 0; + for (auto i = 0; i < count; ++i) { + float deviation = _values[i] - _mean; + deviationTotal += deviation*deviation; + } + _stdDev = sqrt(deviationTotal / (float)count); +} + +QVariantList FrameTimingsScriptingInterface::getValues() const { + QVariantList result; + for (const auto& v : _values) { + result << v; + } + return result; +} diff --git a/interface/src/FrameTimingsScriptingInterface.h b/interface/src/FrameTimingsScriptingInterface.h new file mode 100644 index 0000000000..73eb1aec67 --- /dev/null +++ b/interface/src/FrameTimingsScriptingInterface.h @@ -0,0 +1,38 @@ +// +// Created by Bradley Austin Davis on 2016/04/04 +// 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 +// + +#pragma once +#include +#include + +class FrameTimingsScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(float mean READ getMean CONSTANT) + Q_PROPERTY(float max READ getMax CONSTANT) + Q_PROPERTY(float min READ getMin CONSTANT) + Q_PROPERTY(float standardDeviation READ getStandardDeviation CONSTANT) +public: + Q_INVOKABLE void start(); + Q_INVOKABLE void addValue(uint64_t value); + Q_INVOKABLE void finish(); + Q_INVOKABLE QVariantList getValues() const; + + + uint64_t getMax() const { return _max; } + uint64_t getMin() const { return _min; } + float getStandardDeviation() const { return _stdDev; } + float getMean() const { return _mean; } + +protected: + std::vector _values; + bool _active { false }; + uint64_t _max { 0 }; + uint64_t _min { 0 }; + float _stdDev { 0 }; + float _mean { 0 }; +}; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 906f7b4c9f..4472abe6c3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -204,6 +204,15 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } +void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { + Avatar::setOrientation(quatFromVariant(newOrientationVar)); +} + +QVariant MyAvatar::getOrientationVar() const { + return quatToVariant(Avatar::getOrientation()); +} + + // virtual void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 92bf9e7614..b15f812197 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -105,6 +105,10 @@ public: // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); + Q_INVOKABLE QVariant getOrientationVar() const; + + // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 097d7f73cd..0514411d7f 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -52,7 +52,8 @@ GLBackend::GLTexture::GLTexture(const Texture& texture) : _target(gpuToGLTextureType(texture)), _size(0), _virtualSize(0), - _numLevels(texture.maxMip() + 1), + _numLevels(1), + //_numLevels(texture.maxMip() + 1), _gpuTexture(texture) { Backend::incrementTextureGPUCount(); @@ -254,7 +255,7 @@ void GLBackend::GLTexture::transfer() const { } if (_gpuTexture.isAutogenerateMips()) { - glGenerateMipmap(_target); + //glGenerateMipmap(_target); (void)CHECK_GL_ERROR(); } } @@ -435,7 +436,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + bindingSlot); glBindTexture(object->_target, object->_texture); - glGenerateMipmap(object->_target); + //glGenerateMipmap(object->_target); if (freeSlot < 0) { // If had to use slot 0 then restore state