From e076b32f9c29160a744d29e167428f52446e52e8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Mar 2017 10:15:53 -0800 Subject: [PATCH 01/32] don't automatically unhook overlays from hands unless they were grabbable overlays --- scripts/system/controllers/handControllerGrab.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d982a032cc..cd18d6f932 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3213,13 +3213,21 @@ function MyController(hand) { } _this.previouslyUnhooked[childID] = now; - // we don't know if it's an entity or an overlay + if (Overlays.getProperty(childID, "grabbable")) { + // only auto-unhook overlays that were flagged as grabbable. this avoids unhooking overlays + // used in tutorial. + Overlays.editOverlay(childID, { + parentID: previousParentID, + parentJointIndex: previousParentJointIndex + }); + } Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex }); - Overlays.editOverlay(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex }); } else { Entities.editEntity(childID, { parentID: NULL_UUID }); - Overlays.editOverlay(childID, { parentID: NULL_UUID }); + if (Overlays.getProperty(childID, "grabbable")) { + Overlays.editOverlay(childID, { parentID: NULL_UUID }); + } } } }); From 74323cb1f0065e3e41c97b8c615cdb12de50f370 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Mar 2017 11:45:49 -0800 Subject: [PATCH 02/32] remove debug print --- scripts/system/controllers/handControllerGrab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index cd18d6f932..5b99738a78 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3195,7 +3195,6 @@ function MyController(hand) { // we appear to be holding something and this script isn't in a state that would be holding something. // unhook it. if we previously took note of this entity's parent, put it back where it was. This // works around some problems that happen when more than one hand or avatar is passing something around. - print("disconnecting stray child of hand: (" + _this.hand + ") " + childID); if (_this.previousParentID[childID]) { var previousParentID = _this.previousParentID[childID]; var previousParentJointIndex = _this.previousParentJointIndex[childID]; From c52c43e23d27142118fae920b37970f61f67cfc1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 6 Mar 2017 19:35:00 -0800 Subject: [PATCH 03/32] Remove weighted offset, special case downward pressure --- .../animation/src/AnimInverseKinematics.cpp | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 173af3fdf6..fa8e4654f6 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -488,13 +488,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // measure new _hipsOffset for next frame // by looking for discrepancies between where a targeted endEffector is // and where it wants to be (after IK solutions are done) - - // use weighted average between HMD and other targets - float HMD_WEIGHT = 10.0f; - float OTHER_WEIGHT = 1.0f; - float totalWeight = 0.0f; - - glm::vec3 additionalHipsOffset = Vectors::ZERO; + glm::vec3 newHipsOffset = Vectors::ZERO; for (auto& target: targets) { int targetIndex = target.getIndex(); if (targetIndex == _headIndex && _headIndex != -1) { @@ -505,42 +499,34 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans(); glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans(); const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; - additionalHipsOffset += (OTHER_WEIGHT * HEAD_OFFSET_SLAVE_FACTOR) * (under- actual); - totalWeight += OTHER_WEIGHT; + newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under); } else if (target.getType() == IKTarget::Type::HmdHead) { + // we want to shift the hips to bring the head to its designated position glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans(); - glm::vec3 thisOffset = target.getTranslation() - actual; - glm::vec3 futureHipsOffset = _hipsOffset + thisOffset; - if (glm::length(glm::vec2(futureHipsOffset.x, futureHipsOffset.z)) < _maxHipsOffsetLength) { - // it is imperative to shift the hips and bring the head to its designated position - // so we slam newHipsOffset here and ignore all other targets - additionalHipsOffset = futureHipsOffset - _hipsOffset; - totalWeight = 0.0f; - break; - } else { - additionalHipsOffset += HMD_WEIGHT * (target.getTranslation() - actual); - totalWeight += HMD_WEIGHT; - } + _hipsOffset += target.getTranslation() - actual; + // and ignore all other targets + newHipsOffset = _hipsOffset; + break; + } else if (target.getType() == IKTarget::Type::RotationAndPosition) { + glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans(); + glm::vec3 targetPosition = target.getTranslation(); + newHipsOffset += targetPosition - actualPosition; + + // Add downward pressure on the hips + newHipsOffset *= 0.95f; + newHipsOffset -= 1.0f; } } else if (target.getType() == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans(); glm::vec3 targetPosition = target.getTranslation(); - additionalHipsOffset += OTHER_WEIGHT * (targetPosition - actualPosition); - totalWeight += OTHER_WEIGHT; + newHipsOffset += targetPosition - actualPosition; } } - if (totalWeight > 1.0f) { - additionalHipsOffset /= totalWeight; - } - - // Add downward pressure on the hips - additionalHipsOffset *= 0.95f; - additionalHipsOffset -= 1.0f; // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.10f; float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f; - _hipsOffset += additionalHipsOffset * tau; + _hipsOffset += (newHipsOffset - _hipsOffset) * tau; // clamp the hips offset float hipsOffsetLength = glm::length(_hipsOffset); From 8ae6f2727d4e0faef0af43bb6b853fdd3dac17c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 7 Mar 2017 17:52:55 -0800 Subject: [PATCH 04/32] add support for double click messages to overlays and entities --- interface/src/Application.cpp | 18 +++++++- interface/src/Application.h | 2 +- interface/src/ui/overlays/Overlays.cpp | 20 ++++++++ interface/src/ui/overlays/Overlays.h | 3 ++ .../src/EntityTreeRenderer.cpp | 46 +++++++++++++++++++ .../src/EntityTreeRenderer.h | 3 ++ libraries/shared/src/PointerEvent.cpp | 5 ++ libraries/shared/src/PointerEvent.h | 7 +-- 8 files changed, 99 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..671c530b27 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3174,7 +3174,23 @@ void Application::mousePressEvent(QMouseEvent* event) { } } -void Application::mouseDoublePressEvent(QMouseEvent* event) const { +void Application::mouseDoublePressEvent(QMouseEvent* event) { + auto offscreenUi = DependencyManager::get(); + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QMouseEvent mappedEvent(event->type(), + transformedPos, + event->screenPos(), event->button(), + event->buttons(), event->modifiers()); + + if (!_aboutToQuit) { + getOverlays().mouseDoublePressEvent(&mappedEvent); + if (!_controllerScriptingInterface->areEntityClicksCaptured()) { + getEntities()->mouseDoublePressEvent(&mappedEvent); + } + } + + // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isMouseCaptured()) { return; diff --git a/interface/src/Application.h b/interface/src/Application.h index c4ba760153..98080783a6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -494,7 +494,7 @@ private: void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); - void mouseDoublePressEvent(QMouseEvent* event) const; + void mouseDoublePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void touchBeginEvent(QTouchEvent* event); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ad7fbd6cc2..f40dd522c4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -769,6 +769,26 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { return false; } +bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { + PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); + + PickRay ray = qApp->computePickRay(event->x(), event->y()); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + if (rayPickResult.intersects) { + _currentClickingOnOverlayID = rayPickResult.overlayID; + + // Only Web overlays can have focus. + auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + if (thisOverlay) { + auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); + emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; + } + } + emit mouseDoublePressOffOverlay(); + return false; +} + bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 5c22e46880..c35c7c1ced 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -101,6 +101,7 @@ public: OverlayID addOverlay(Overlay::Pointer overlay); bool mousePressEvent(QMouseEvent* event); + bool mouseDoublePressEvent(QMouseEvent* event); bool mouseReleaseEvent(QMouseEvent* event); bool mouseMoveEvent(QMouseEvent* event); @@ -300,9 +301,11 @@ signals: void panelDeleted(OverlayID id); void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); + void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event); void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); void mousePressOffOverlay(); + void mouseDoublePressOffOverlay(); void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event); void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index bd25bcf905..27e00b47c6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -735,6 +735,52 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { } } +void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { + // If we don't have a tree, or we're in the process of shutting down, then don't + // process these events. + if (!_tree || _shuttingDown) { + return; + } + PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent"); + PickRay ray = _viewState->computePickRay(event->x(), event->y()); + + bool precisionPicking = !_dontDoPrecisionPicking; + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); + if (rayPickResult.intersects) { + //qCDebug(entitiesrenderer) << "mouseDoublePressEvent over entity:" << rayPickResult.entityID; + + QString urlString = rayPickResult.properties.getHref(); + QUrl url = QUrl(urlString, QUrl::StrictMode); + if (url.isValid() && !url.isEmpty()){ + DependencyManager::get()->handleLookupString(urlString); + } + + glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit mouseDoublePressOnEntity(rayPickResult.entityID, pointerEvent); + + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseDoublePressOnEntity", pointerEvent); + } + + _currentClickingOnEntityID = rayPickResult.entityID; + emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "doubleclickOnEntity", pointerEvent); + } + + _lastPointerEvent = pointerEvent; + _lastPointerEventValid = true; + + } else { + emit mouseDoublePressOffEntity(); + } +} + void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index c11738c459..753f25310c 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -90,6 +90,7 @@ public: // event handles which may generate entity related events void mouseReleaseEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); + void mouseDoublePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); /// connect our signals to anEntityScriptingInterface for firing of events related clicking, @@ -103,9 +104,11 @@ public: signals: void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mouseDoublePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mousePressOffEntity(); + void mouseDoublePressOffEntity(); void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index ed9acb9ada..e429204e94 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -47,6 +47,9 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve case Press: obj.setProperty("type", "Press"); break; + case DoublePress: + obj.setProperty("type", "DoublePress"); + break; case Release: obj.setProperty("type", "Release"); break; @@ -128,6 +131,8 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve QString typeStr = type.isString() ? type.toString() : "Move"; if (typeStr == "Press") { event._type = Press; + } else if (typeStr == "DoublePress") { + event._type = DoublePress; } else if (typeStr == "Release") { event._type = Release; } else { diff --git a/libraries/shared/src/PointerEvent.h b/libraries/shared/src/PointerEvent.h index 054835c4fc..166c4be592 100644 --- a/libraries/shared/src/PointerEvent.h +++ b/libraries/shared/src/PointerEvent.h @@ -26,9 +26,10 @@ public: }; enum EventType { - Press, // A button has just been pressed - Release, // A button has just been released - Move // The pointer has just moved + Press, // A button has just been pressed + DoublePress, // A button has just been double pressed + Release, // A button has just been released + Move // The pointer has just moved }; PointerEvent(); From d7c3677b2211bd3b906bbf40c1d23b61f827b2d5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 7 Mar 2017 17:55:03 -0800 Subject: [PATCH 05/32] add example --- .../entityScripts/doubleClickExample.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 script-archive/entityScripts/doubleClickExample.js diff --git a/script-archive/entityScripts/doubleClickExample.js b/script-archive/entityScripts/doubleClickExample.js new file mode 100644 index 0000000000..daff2668ed --- /dev/null +++ b/script-archive/entityScripts/doubleClickExample.js @@ -0,0 +1,19 @@ +(function() { + var _this; + function DoubleClickExample() { + _this = this; + return; + } + + DoubleClickExample.prototype = { + clickDownOnEntity: function() { + print("clickDownOnEntity"); + }, + + doubleclickOnEntity: function() { + print("doubleclickOnEntity"); + } + + }; + return new DoubleClickExample(); +}); \ No newline at end of file From 7acfcf6750704b8b76523cf23d64f2291477df6a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 8 Mar 2017 18:15:32 -0800 Subject: [PATCH 06/32] fade in and out when opening/closing the noise gate --- libraries/audio-client/src/AudioNoiseGate.cpp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index ac45f98f0b..0ced518097 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -142,7 +142,11 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { _sampleCounter = 0; } + + bool isOpeningGate = false; + if (samplesOverNoiseGate > NOISE_GATE_WIDTH) { + isOpeningGate = !_isOpen; _isOpen = true; _framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY; } else { @@ -154,7 +158,23 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { } } if (!_isOpen) { - memset(samples, 0, numSamples * sizeof(int16_t)); + if (_closedInLastFrame) { + // would be nice to do a little crossfade from silence + for (int i = 0; i < numSamples; i++) { + float fadedSample = (1.0f - (float)i / (float)numSamples) * (float)samples[i]; + samples[i] = (int16_t)fadedSample; + } + } else { + memset(samples, 0, numSamples * sizeof(int16_t)); + } _lastLoudness = 0; } + + if (isOpeningGate) { + // would be nice to do a little crossfade from silence + for (int i = 0; i < numSamples; i++) { + float fadedSample = ((float)i / (float)numSamples) * (float)samples[i]; + samples[i] = (int16_t)fadedSample; + } + } } From ffe69acb5cb0ec4acc53a3a2ab4d5c25c2b633dd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 8 Mar 2017 19:26:12 -0800 Subject: [PATCH 07/32] fix rare physics crash --- libraries/physics/src/PhysicsEngine.cpp | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index f57be4eab3..1073f4b1cf 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -143,12 +143,35 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { } void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { - // first bump and prune contacts for all objects in the list + // bump and prune contacts for all objects in the list for (auto object : objects) { bumpAndPruneContacts(object); } - // then remove them + if (_activeStaticBodies.size() > 0) { + // very unlikely to get here but its theoretically possible: + // immediately after a static entity was moved we skipped a simulation step and never got around to + // updating the static entity's RigidBody AABB. Now we're deleting bodies ==> we must scan + // _activeStaticBodies for bodies being removed and clear any that we find. + for (auto object : objects) { + btRigidBody* body = object->getRigidBody(); + + std::vector::reverse_iterator itr = _activeStaticBodies.rbegin(); + while (itr != _activeStaticBodies.rend()) { + if (body == *itr) { + if (*itr != *(_activeStaticBodies.rbegin())) { + // swap with rbegin + *itr = *(_activeStaticBodies.rbegin()); + } + _activeStaticBodies.pop_back(); + break; + } + ++itr; + } + } + } + + // remove bodies for (auto object : objects) { btRigidBody* body = object->getRigidBody(); if (body) { From 2c141fd5552565a8c5e14fa6f2382b3080113c25 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Mar 2017 09:18:29 -0800 Subject: [PATCH 08/32] more correct comment --- libraries/physics/src/PhysicsEngine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 1073f4b1cf..363887de25 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -149,10 +149,10 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { } if (_activeStaticBodies.size() > 0) { - // very unlikely to get here but its theoretically possible: - // immediately after a static entity was moved we skipped a simulation step and never got around to - // updating the static entity's RigidBody AABB. Now we're deleting bodies ==> we must scan - // _activeStaticBodies for bodies being removed and clear any that we find. + // _activeStaticBodies was not cleared last frame. + // The only way to get here is if a static object were moved but we did not actually step the simulation last + // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan + // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer. for (auto object : objects) { btRigidBody* body = object->getRigidBody(); From 69fdea36216e0f957fb3812dd0ee84046243ce86 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 11:44:53 -0800 Subject: [PATCH 09/32] fix typo in comment --- libraries/audio-client/src/AudioNoiseGate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 45a70c4a28..5224849c5b 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -155,7 +155,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { } if (!_isOpen) { if (_closedInLastFrame) { - // would be nice to do a little crossfade from silence + // would be nice to do a little crossfade to silence for (int i = 0; i < numSamples; i++) { float fadedSample = (1.0f - (float)i / (float)numSamples) * (float)samples[i]; samples[i] = (int16_t)fadedSample; From eaea718de1994149d5a2bb774bc60f88bbc98cc7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 17:08:19 -0800 Subject: [PATCH 10/32] add various scriping interfaces for managing the audio scope and detecting noise gate transitions --- interface/src/Application.cpp | 8 ++++++++ interface/src/audio/AudioScope.cpp | 14 ++++++++------ interface/src/audio/AudioScope.h | 9 +++++++-- libraries/audio-client/src/AudioClient.cpp | 8 ++++++++ libraries/audio-client/src/AudioClient.h | 2 ++ libraries/audio-client/src/AudioNoiseGate.cpp | 9 +++++---- libraries/audio-client/src/AudioNoiseGate.h | 2 ++ .../script-engine/src/AudioScriptingInterface.h | 11 +++++++---- 8 files changed, 47 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f870bd9f83..7e3f61e0ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1182,6 +1182,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the local loopback interface for local sounds AudioInjector::setLocalAudioInterface(audioIO.data()); AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); + connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened); + connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed); + connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived); + this->installEventFilter(this); @@ -1947,6 +1951,9 @@ void Application::initializeUi() { rootContext->setContextProperty("ApplicationInterface", this); rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); + //rootContext->setContextProperty("AudioScope", DependencyManager::get.data()); + + rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); _fileDownload = new FileScriptingInterface(engine); @@ -5521,6 +5528,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get()->getStats().data()); + scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get().data()); // Caches scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp index 346fbd11f4..cf9984e32b 100644 --- a/interface/src/audio/AudioScope.cpp +++ b/interface/src/audio/AudioScope.cpp @@ -52,12 +52,14 @@ AudioScope::AudioScope() : connect(audioIO.data(), &AudioClient::inputReceived, this, &AudioScope::addInputToScope); } -void AudioScope::toggle() { - _isEnabled = !_isEnabled; - if (_isEnabled) { - allocateScope(); - } else { - freeScope(); +void AudioScope::setVisible(bool visible) { + if (_isEnabled != visible) { + _isEnabled = visible; + if (_isEnabled) { + allocateScope(); + } else { + freeScope(); + } } } diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index 0b716d7666..615bdaf17f 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -34,8 +34,14 @@ public: void render(RenderArgs* renderArgs, int width, int height); public slots: - void toggle(); + void toggle() { setVisible(!_isEnabled); } + void setVisible(bool visible); + bool getVisible() const { return _isEnabled; } + void togglePause() { _isPaused = !_isPaused; } + void setPause(bool paused) { _isPaused = paused; } + bool getPause() { return _isPaused; } + void selectAudioScopeFiveFrames(); void selectAudioScopeTwentyFrames(); void selectAudioScopeFiftyFrames(); @@ -74,7 +80,6 @@ private: int _inputID; int _outputLeftID; int _outputRightD; - }; #endif // hifi_AudioScope_h diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 9cd87d2e70..6d135dc571 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1024,6 +1024,7 @@ void AudioClient::handleAudioInput() { if (_inputGate.clippedInLastFrame()) { _timeSinceLastClip = 0.0f; } + } else { float loudness = 0.0f; @@ -1041,6 +1042,13 @@ void AudioClient::handleAudioInput() { emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); + if (_inputGate.openedInLastFrame()) { + emit noiseGateOpened(); + } + if (_inputGate.closedInLastFrame()) { + emit noiseGateClosed(); + } + } else { // our input loudness is 0, since we're muted _lastInputLoudness = 0; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5619051eaf..57b19acceb 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -218,6 +218,8 @@ signals: void inputReceived(const QByteArray& inputSamples); void outputBytesToNetwork(int numBytes); void inputBytesFromNetwork(int numBytes); + void noiseGateOpened(); + void noiseGateClosed(); void changeDevice(const QAudioDeviceInfo& outputDeviceInfo); void deviceChanged(); diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 5224849c5b..17bbd1a509 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -141,15 +141,16 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { } - bool isOpeningGate = false; + _closedInLastFrame = false; + _openedInLastFrame = false; if (samplesOverNoiseGate > NOISE_GATE_WIDTH) { - isOpeningGate = !_isOpen; + _openedInLastFrame = !_isOpen; _isOpen = true; _framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY; } else { if (--_framesToClose == 0) { - _closedInLastFrame = !_isOpen; + _closedInLastFrame = _isOpen; _isOpen = false; } } @@ -166,7 +167,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { _lastLoudness = 0; } - if (isOpeningGate) { + if (_openedInLastFrame) { // would be nice to do a little crossfade from silence for (int i = 0; i < numSamples; i++) { float fadedSample = ((float)i / (float)numSamples) * (float)samples[i]; diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio-client/src/AudioNoiseGate.h index 774a4157bb..ba1ab8599c 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio-client/src/AudioNoiseGate.h @@ -25,6 +25,7 @@ public: bool clippedInLastFrame() const { return _didClipInLastFrame; } bool closedInLastFrame() const { return _closedInLastFrame; } + bool openedInLastFrame() const { return _openedInLastFrame; } float getMeasuredFloor() const { return _measuredFloor; } float getLastLoudness() const { return _lastLoudness; } @@ -42,6 +43,7 @@ private: int _sampleCounter; bool _isOpen; bool _closedInLastFrame { false }; + bool _openedInLastFrame { false }; int _framesToClose; }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 07a6b171f4..6cce78d48f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -32,10 +32,13 @@ protected: Q_INVOKABLE void setStereoInput(bool stereo); signals: - void mutedByMixer(); - void environmentMuted(); - void receivedFirstPacket(); - void disconnected(); + void mutedByMixer(); /// the client has been muted by the mixer + void environmentMuted(); /// the entire environment has been muted by the mixer + void receivedFirstPacket(); /// the client has received its first packet from the audio mixer + void disconnected(); /// the client has been disconnected from the audio mixer + void noiseGateOpened(); /// the noise gate has opened + void noiseGateClosed(); /// the noise gate has closed + void inputReceived(const QByteArray& inputSamples); /// a frame of mic input audio has been received and processed private: AudioScriptingInterface(); From 5f3ee2e98f1285f64d392b07d8832caf5b9aa95b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 17:08:54 -0800 Subject: [PATCH 11/32] add a basic tablet app for the audio scope --- scripts/system/audioScope.js | 97 ++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 scripts/system/audioScope.js diff --git a/scripts/system/audioScope.js b/scripts/system/audioScope.js new file mode 100644 index 0000000000..0bcff7d5be --- /dev/null +++ b/scripts/system/audioScope.js @@ -0,0 +1,97 @@ +"use strict"; +// +// audioScope.js +// scripts/system/ +// +// Created by Brad Hefta-Gaub on 3/10/2016 +// Copyright 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 +// +/* global Script, Tablet, AudioScope, Audio */ + +(function () { // BEGIN LOCAL_SCOPE + + var framesSinceOpened = 0; + var scopeVisibile = AudioScope.getVisible(); + var scopePaused = AudioScope.getPause(); + var autoPause = false; + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var showScopeButton = tablet.addButton({ + icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", + text: "Audio Scope", + isActive: scopeVisibile + }); + + var pauseScopeButton = tablet.addButton({ + icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", + text: "Pause", + isActive: scopePaused + }); + + var autoPauseScopeButton = tablet.addButton({ + icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", + text: "Auto Pause", + isActive: autoPause + }); + + function setScopePause(paused) { + scopePaused = paused; + pauseScopeButton.editProperties({ + isActive: scopePaused, + text: scopePaused ? "Unpause" : "Pause" + }); + AudioScope.setPause(scopePaused); + } + + showScopeButton.clicked.connect(function () { + // toggle button active state + scopeVisibile = !scopeVisibile; + showScopeButton.editProperties({ + isActive: scopeVisibile + }); + + AudioScope.setVisible(scopeVisibile); + }); + + pauseScopeButton.clicked.connect(function () { + // toggle button active state + setScopePause(!scopePaused); + }); + + autoPauseScopeButton.clicked.connect(function () { + // toggle button active state + autoPause = !autoPause; + autoPauseScopeButton.editProperties({ + isActive: autoPause, + text: autoPause ? "Auto Pause" : "Manual" + }); + }); + + Script.scriptEnding.connect(function () { + tablet.removeButton(showScopeButton); + tablet.removeButton(pauseScopeButton); + tablet.removeButton(autoPauseScopeButton); + }); + + Audio.noiseGateOpened.connect(function(){ + framesSinceOpened = 0; + }); + + Audio.noiseGateClosed.connect(function(){ + // noise gate closed + }); + + Audio.inputReceived.connect(function(){ + if (autoPause && AudioScope.getVisible()) { + framesSinceOpened++; + if (framesSinceOpened > 50) { + setScopePause(true); + } + } + }); + + +}()); // END LOCAL_SCOPE \ No newline at end of file From b8a2525f9e04a7c8bec8c56463f089d15375daa9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 17:38:00 -0800 Subject: [PATCH 12/32] add auto pause/resume to the Audio Scope App --- scripts/system/audioScope.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/scripts/system/audioScope.js b/scripts/system/audioScope.js index 0bcff7d5be..85060173d4 100644 --- a/scripts/system/audioScope.js +++ b/scripts/system/audioScope.js @@ -13,7 +13,6 @@ (function () { // BEGIN LOCAL_SCOPE - var framesSinceOpened = 0; var scopeVisibile = AudioScope.getVisible(); var scopePaused = AudioScope.getPause(); var autoPause = false; @@ -77,21 +76,16 @@ }); Audio.noiseGateOpened.connect(function(){ - framesSinceOpened = 0; + if (autoPause) { + setScopePause(false); + } }); Audio.noiseGateClosed.connect(function(){ // noise gate closed - }); - - Audio.inputReceived.connect(function(){ - if (autoPause && AudioScope.getVisible()) { - framesSinceOpened++; - if (framesSinceOpened > 50) { - setScopePause(true); - } + if (autoPause) { + setScopePause(true); } }); - }()); // END LOCAL_SCOPE \ No newline at end of file From 7eb4b8f5f73f8c39c5cdc5219d96c2555a196c25 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 17:38:13 -0800 Subject: [PATCH 13/32] expose AudioScope to QML --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7e3f61e0ec..645c6ab15f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1951,7 +1951,7 @@ void Application::initializeUi() { rootContext->setContextProperty("ApplicationInterface", this); rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); - //rootContext->setContextProperty("AudioScope", DependencyManager::get.data()); + rootContext->setContextProperty("AudioScope", DependencyManager::get().data()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); From 23e47f2fdc8de48290a0a7dd8f0847213b2e121c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 18:13:35 -0800 Subject: [PATCH 14/32] added icons to the audioScope app --- .../icons/tablet-icons/scope-auto.svg | 46 +++++++++++++++++++ .../icons/tablet-icons/scope-pause.svg | 30 ++++++++++++ .../icons/tablet-icons/scope-play.svg | 30 ++++++++++++ scripts/system/audioScope.js | 12 +++-- 4 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 interface/resources/icons/tablet-icons/scope-auto.svg create mode 100644 interface/resources/icons/tablet-icons/scope-pause.svg create mode 100644 interface/resources/icons/tablet-icons/scope-play.svg diff --git a/interface/resources/icons/tablet-icons/scope-auto.svg b/interface/resources/icons/tablet-icons/scope-auto.svg new file mode 100644 index 0000000000..85ef3f0e38 --- /dev/null +++ b/interface/resources/icons/tablet-icons/scope-auto.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/scope-pause.svg b/interface/resources/icons/tablet-icons/scope-pause.svg new file mode 100644 index 0000000000..3fe74fcc9f --- /dev/null +++ b/interface/resources/icons/tablet-icons/scope-pause.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/scope-play.svg b/interface/resources/icons/tablet-icons/scope-play.svg new file mode 100644 index 0000000000..56d90ef38a --- /dev/null +++ b/interface/resources/icons/tablet-icons/scope-play.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/audioScope.js b/scripts/system/audioScope.js index 85060173d4..81d8e8fbd4 100644 --- a/scripts/system/audioScope.js +++ b/scripts/system/audioScope.js @@ -19,19 +19,22 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var showScopeButton = tablet.addButton({ - icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", + icon: "icons/tablet-icons/scope.svg", text: "Audio Scope", isActive: scopeVisibile }); + var scopePauseImage = "icons/tablet-icons/scope-pause.svg"; + var scopePlayImage = "icons/tablet-icons/scope-play.svg"; + var pauseScopeButton = tablet.addButton({ - icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", - text: "Pause", + icon: scopePaused ? scopePlayImage : scopePauseImage, + text: scopePaused ? "Unpause" : "Pause", isActive: scopePaused }); var autoPauseScopeButton = tablet.addButton({ - icon: "http://s3.amazonaws.com/hifi-public/tony/icons/star.png", + icon: "icons/tablet-icons/scope-auto.svg", text: "Auto Pause", isActive: autoPause }); @@ -40,6 +43,7 @@ scopePaused = paused; pauseScopeButton.editProperties({ isActive: scopePaused, + icon: scopePaused ? scopePlayImage : scopePauseImage, text: scopePaused ? "Unpause" : "Pause" }); AudioScope.setPause(scopePaused); From aa65f03509f9a56f5e060c1e95025283b78b377f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 18:14:00 -0800 Subject: [PATCH 15/32] fix mac warning and remove accidentally added whitespace --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - interface/src/Application.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c10a616818..d2bfdde7ea 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -37,7 +37,6 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; // FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now. const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; -const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000; AvatarMixer::AvatarMixer(ReceivedMessage& message) : ThreadedAssignment(message) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 645c6ab15f..60d9e815f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1953,7 +1953,6 @@ void Application::initializeUi() { rootContext->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); rootContext->setContextProperty("AudioScope", DependencyManager::get().data()); - rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); _fileDownload = new FileScriptingInterface(engine); From 6ce50269ac62278f13d1d4253b0427b575234766 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 10 Mar 2017 18:42:18 -0800 Subject: [PATCH 16/32] add audio mixer in/out stats to the client stats --- interface/resources/qml/Stats.qml | 10 ++++++++++ interface/src/ui/Stats.cpp | 6 ++++++ interface/src/ui/Stats.h | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 58d589b667..ec170dfd08 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -181,6 +181,16 @@ Item { root.avatarMixerOutPps + "pps, " + root.myAvatarSendRate.toFixed(2) + "hz"; } + StatText { + visible: root.expanded; + text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " + + root.audioMixerInPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + + root.audioMixerOutPps + "pps"; + } StatText { visible: root.expanded; text: "Downloads: " + root.downloads + "/" + root.downloadLimit + diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1075bbdaa4..038dcd42cc 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -214,6 +214,12 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerPps, roundf( bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + + STAT_UPDATE(audioMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 6be084100c..96c5d374d3 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -70,6 +70,12 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, avatarMixerOutKbps, 0) STATS_PROPERTY(int, avatarMixerOutPps, 0) STATS_PROPERTY(float, myAvatarSendRate, 0) + + STATS_PROPERTY(int, audioMixerInKbps, 0) + STATS_PROPERTY(int, audioMixerInPps, 0) + STATS_PROPERTY(int, audioMixerOutKbps, 0) + STATS_PROPERTY(int, audioMixerOutPps, 0) + STATS_PROPERTY(int, audioMixerKbps, 0) STATS_PROPERTY(int, audioMixerPps, 0) STATS_PROPERTY(int, downloads, 0) @@ -180,6 +186,10 @@ signals: void avatarMixerOutKbpsChanged(); void avatarMixerOutPpsChanged(); void myAvatarSendRateChanged(); + void audioMixerInKbpsChanged(); + void audioMixerInPpsChanged(); + void audioMixerOutKbpsChanged(); + void audioMixerOutPpsChanged(); void audioMixerKbpsChanged(); void audioMixerPpsChanged(); void downloadsChanged(); From a38965d628b09dfe179c1dccef4c56059a8e37fb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 11 Mar 2017 09:50:30 -0800 Subject: [PATCH 17/32] add more audio stats --- interface/resources/qml/Stats.qml | 15 ++++++++++ interface/src/ui/Stats.cpp | 21 ++++++++++++-- interface/src/ui/Stats.h | 15 +++++++++- libraries/audio-client/src/AudioClient.cpp | 14 ++++++++++ libraries/audio-client/src/AudioClient.h | 14 ++++++++++ libraries/audio-client/src/AudioNoiseGate.h | 1 + libraries/shared/src/shared/RateCounter.h | 31 ++++++++++++--------- 7 files changed, 94 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index ec170dfd08..564c74b526 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -186,11 +186,26 @@ Item { text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " + root.audioMixerInPps + "pps"; } + StatText { + visible: root.expanded; + text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + "Silent: " + root.audioSilentInboundPPS + " pps"; + } StatText { visible: root.expanded; text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + root.audioMixerOutPps + "pps"; } + StatText { + visible: root.expanded; + text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " + + "Silent: " + root.audioSilentOutboundPPS + " pps"; + } + StatText { + visible: root.expanded; + text: "Audio Codec: " + root.audioCodec + " Noise Gate: " + + root.audioNoiseGate; + } StatText { visible: root.expanded; text: "Downloads: " + root.downloads + "/" + root.downloadLimit + diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 038dcd42cc..923d9f642d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -198,15 +198,16 @@ void Stats::updateStats(bool force) { STAT_UPDATE(avatarMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); - STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate()); } else { STAT_UPDATE(avatarMixerInKbps, -1); STAT_UPDATE(avatarMixerInPps, -1); STAT_UPDATE(avatarMixerOutKbps, -1); STAT_UPDATE(avatarMixerOutPps, -1); - STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate()); } + STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate()); + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + auto audioClient = DependencyManager::get(); if (audioMixerNode || force) { STAT_UPDATE(audioMixerKbps, roundf( bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + @@ -219,11 +220,25 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); - + STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS()); + STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS()); + STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS()); + STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS()); } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); + STAT_UPDATE(audioMixerInKbps, -1); + STAT_UPDATE(audioMixerInPps, -1); + STAT_UPDATE(audioMixerOutKbps, -1); + STAT_UPDATE(audioMixerOutPps, -1); + STAT_UPDATE(audioMicOutboundPPS, -1); + STAT_UPDATE(audioSilentOutboundPPS, -1); + STAT_UPDATE(audioAudioInboundPPS, -1); + STAT_UPDATE(audioSilentInboundPPS, -1); } + STAT_UPDATE(audioCodec, audioClient->getSelectedAudioFormat()); + STAT_UPDATE(audioNoiseGate, audioClient->getNoiseGateOpen() ? "Open" : "Closed"); + auto loadingRequests = ResourceCache::getLoadingRequests(); STAT_UPDATE(downloads, loadingRequests.size()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 96c5d374d3..0ce113e0a0 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -75,9 +75,15 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, audioMixerInPps, 0) STATS_PROPERTY(int, audioMixerOutKbps, 0) STATS_PROPERTY(int, audioMixerOutPps, 0) - STATS_PROPERTY(int, audioMixerKbps, 0) STATS_PROPERTY(int, audioMixerPps, 0) + STATS_PROPERTY(int, audioMicOutboundPPS, 0) + STATS_PROPERTY(int, audioSilentOutboundPPS, 0) + STATS_PROPERTY(int, audioAudioInboundPPS, 0) + STATS_PROPERTY(int, audioSilentInboundPPS, 0) + STATS_PROPERTY(QString, audioCodec, QString()) + STATS_PROPERTY(QString, audioNoiseGate, QString()) + STATS_PROPERTY(int, downloads, 0) STATS_PROPERTY(int, downloadLimit, 0) STATS_PROPERTY(int, downloadsPending, 0) @@ -192,6 +198,13 @@ signals: void audioMixerOutPpsChanged(); void audioMixerKbpsChanged(); void audioMixerPpsChanged(); + void audioMicOutboundPPSChanged(); + void audioSilentOutboundPPSChanged(); + void audioAudioInboundPPSChanged(); + void audioSilentInboundPPSChanged(); + void audioCodecChanged(); + void audioNoiseGateChanged(); + void downloadsChanged(); void downloadLimitChanged(); void downloadsPendingChanged(); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6d135dc571..9fb8bae6ca 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -608,6 +608,13 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer message) { + + if (message->getType() == PacketType::SilentAudioFrame) { + _silentInbound.increment(); + } else { + _audioInbound.increment(); + } + auto nodeList = DependencyManager::get(); nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket); @@ -1067,7 +1074,11 @@ void AudioClient::handleAudioInput() { // have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet if (_lastInputLoudness == 0 && !_inputGate.closedInLastFrame()) { packetType = PacketType::SilentAudioFrame; + _silentOutbound.increment(); + } else { + _micAudioOutbound.increment(); } + Transform audioTransform; audioTransform.setTranslation(_positionGetter()); audioTransform.setRotation(_orientationGetter()); @@ -1092,6 +1103,7 @@ void AudioClient::handleAudioInput() { } } +// FIXME - should this go through the noise gate and honor mute and echo? void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { Transform audioTransform; audioTransform.setTranslation(_positionGetter()); @@ -1104,6 +1116,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { encodedBuffer = audio; } + _micAudioOutbound.increment(); + // FIXME check a flag to see if we should echo audio? emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 57b19acceb..512b4bb3c1 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -46,6 +46,8 @@ #include #include +#include + #include #include "AudioIOStats.h" @@ -121,6 +123,13 @@ public: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); + Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; } + Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); } + Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); } + Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); } + Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); } + Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); } + const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } @@ -384,6 +393,11 @@ private: Encoder* _encoder { nullptr }; // for outbound mic stream QThread* _checkDevicesThread { nullptr }; + + RateCounter<> _silentOutbound; + RateCounter<> _micAudioOutbound; + RateCounter<> _silentInbound; + RateCounter<> _audioInbound; }; diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio-client/src/AudioNoiseGate.h index ba1ab8599c..fb31561994 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio-client/src/AudioNoiseGate.h @@ -26,6 +26,7 @@ public: bool clippedInLastFrame() const { return _didClipInLastFrame; } bool closedInLastFrame() const { return _closedInLastFrame; } bool openedInLastFrame() const { return _openedInLastFrame; } + bool isOpen() const { return _isOpen; } float getMeasuredFloor() const { return _measuredFloor; } float getLastLoudness() const { return _lastLoudness; } diff --git a/libraries/shared/src/shared/RateCounter.h b/libraries/shared/src/shared/RateCounter.h index d04d87493a..3cf509b6bf 100644 --- a/libraries/shared/src/shared/RateCounter.h +++ b/libraries/shared/src/shared/RateCounter.h @@ -24,29 +24,34 @@ public: RateCounter() { _rate = 0; } // avoid use of std::atomic copy ctor void increment(size_t count = 1) { - auto now = usecTimestampNow(); - float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC; - if (currentIntervalMs > (float) INTERVAL) { - float currentCount = _count; - float intervalSeconds = currentIntervalMs / (float) MSECS_PER_SECOND; - _rate = roundf(currentCount / intervalSeconds * _scale) / _scale; - _start = now; - _count = 0; - }; + checkRate(); _count += count; } - float rate() const { return _rate; } + float rate() const { checkRate(); return _rate; } uint8_t precision() const { return PRECISION; } uint32_t interval() const { return INTERVAL; } private: - uint64_t _start { usecTimestampNow() }; - size_t _count { 0 }; + mutable uint64_t _start { usecTimestampNow() }; + mutable size_t _count { 0 }; const float _scale { powf(10, PRECISION) }; - std::atomic _rate; + mutable std::atomic _rate; + + void checkRate() const { + auto now = usecTimestampNow(); + float currentIntervalMs = (now - _start) / (float)USECS_PER_MSEC; + if (currentIntervalMs > (float)INTERVAL) { + float currentCount = _count; + float intervalSeconds = currentIntervalMs / (float)MSECS_PER_SECOND; + _rate = roundf(currentCount / intervalSeconds * _scale) / _scale; + _start = now; + _count = 0; + }; + } + }; #endif From 49055cfa5cab406d864e090b6f8fe81160f7aedb Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Mon, 13 Mar 2017 16:31:29 +0100 Subject: [PATCH 18/32] Some statistics showed --- .../hifi/dialogs/TabletEntityStatistics.qml | 312 ++++++++++ interface/src/Application.cpp | 15 + interface/src/Application.h | 1 + interface/src/Menu.cpp | 4 +- interface/src/ui/OctreeStatsProvider.cpp | 561 ++++++++++++++++++ interface/src/ui/OctreeStatsProvider.h | 128 ++++ interface/src/ui/overlays/Web3DOverlay.cpp | 2 + 7 files changed, 1022 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml create mode 100644 interface/src/ui/OctreeStatsProvider.cpp create mode 100644 interface/src/ui/OctreeStatsProvider.h diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml new file mode 100644 index 0000000000..21f63dd070 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -0,0 +1,312 @@ +// +// TabletEntityStatistics.qml +// +// Created by Vlad Stelmahovsky on 3/11/17 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import Qt.labs.settings 1.0 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../windows" + +Rectangle { + id: root + objectName: "EntityStatistics" + + property var eventBridge; + signal sendToScript(var message); + property bool isHMD: false + + color: hifi.colors.baseGray + + property int colorScheme: hifi.colorSchemes.dark + + HifiConstants { id: hifi } + + Component.onCompleted: { + OctreeStats.startUpdates() + } + + Component.onDestruction: { + OctreeStats.stopUpdates() + } + + Flickable { + id: scrollView + width: parent.width + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.bottomMargin: hifi.dimensions.tabletMenuHeader + contentWidth: column.implicitWidth + contentHeight: column.implicitHeight + + Column { + id: column + anchors.margins: 10 + anchors.left: parent.left + anchors.right: parent.right + y: hifi.dimensions.tabletMenuHeader //-bgNavBar + spacing: 20 + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Elements on Servers:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: elementsOnServerLabel + size: 20 + text: OctreeStats.serverElements + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Local Elements:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: localElementsLabel + size: 20 + text: OctreeStats.localElements + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Elements Memory:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: elementsMemoryLabel + size: 20 + text: OctreeStats.localElementsMemory + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Sending Mode:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: sendingModeLabel + size: 20 + text: OctreeStats.sendingMode + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Incoming Entity Packets:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: incomingEntityPacketsLabel + size: 20 + text: OctreeStats.processedPackets + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Processed Packets Elements:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: processedPacketsElementsLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Processed Packets Entities:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: processedPacketsEntitiesLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Processed Packets Timing:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: processedPacketsTimingLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Outbound Entity Packets:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: outboundEntityPacketsLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Entity Update Time:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: entityUpdateTimeLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + HifiControls.Label { + size: 20 + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Entity Updates:") + colorScheme: root.colorScheme + } + HifiControls.Label { + id: entityUpdatesLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + Repeater { + model: OctreeStats.serversNum + + delegate: Column { + id: serverColumn + width: scrollView.width - 10 + x: 5 + spacing: 5 + + state: "less" + + HifiControls.Label { + size: 20 + width: parent.width + text: qsTr("Entity Server ") + (index+1) + ":" + colorScheme: root.colorScheme + } + HifiControls.Label { + id: entityServer1Label + size: 20 + width: parent.width + colorScheme: root.colorScheme + } + Row { + id: buttonsRow + width: parent.width + height: 30 + spacing: 10 + + HifiControls.Button { + id: moreButton + color: hifi.buttons.blue + colorScheme: root.colorScheme + width: parent.width / 2 - 10 + height: 30 + onClicked: { + if (serverColumn.state === "less") { + serverColumn.state = "more" + } else if (serverColumn.state === "more") { + serverColumn.state = "most" + } else { + serverColumn.state = "less" + } + } + } + HifiControls.Button { + id: mostButton + color: hifi.buttons.blue + colorScheme: root.colorScheme + height: 30 + width: parent.width / 2 - 10 + onClicked: { + if (serverColumn.state === "less") { + serverColumn.state = "most" + } else if (serverColumn.state === "more") { + serverColumn.state = "less" + } else { + serverColumn.state = "more" + } + } + + } + } + states: [ + State { + name: "less" + PropertyChanges { target: moreButton; text: qsTr("more..."); } + PropertyChanges { target: mostButton; text: qsTr("most..."); } + }, + State { + name: "more" + PropertyChanges { target: moreButton; text: qsTr("most..."); } + PropertyChanges { target: mostButton; text: qsTr("less..."); } + }, + State { + name: "most" + PropertyChanges { target: moreButton; text: qsTr("less..."); } + PropertyChanges { target: mostButton; text: qsTr("least..."); } + } + ] + } //servers column + } //repeater + } //column + } //flickable +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dcd47979f1..5faf2b8ff0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -173,6 +173,7 @@ #include "ui/overlays/Overlays.h" #include "Util.h" #include "InterfaceParentFinder.h" +#include "ui/OctreeStatsProvider.h" #include "FrameTimingsScriptingInterface.h" #include @@ -523,6 +524,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); return previousSessionCrashed; } @@ -1803,6 +1805,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); ResourceManager::cleanup(); @@ -6363,6 +6366,18 @@ void Application::loadScriptURLDialog() const { } } + +void Application::loadEntityStatisticsDialog() { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { + auto dialogsManager = DependencyManager::get(); + dialogsManager->octreeStatsDetails(); + } else { + tablet->pushOntoStack("../../hifi/dialogs/TabletEntityStatistics.qml"); + } + +} void Application::toggleLogDialog() { if (! _logDialog) { _logDialog = new LogDialog(nullptr, getLogger()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 9fab4aef81..e4715ac41f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -403,6 +403,7 @@ public slots: void addAssetToWorldMessageClose(); Q_INVOKABLE void toggleMuteAudio(); + void loadEntityStatisticsDialog(); private slots: void showDesktop(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 241f908190..15fc46d6cb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -544,8 +544,10 @@ Menu::Menu() { // Developer > Entities >>> MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities"); + addActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::OctreeStats, 0, - dialogsManager.data(), SLOT(octreeStatsDetails())); + qApp, SLOT(loadEntityStatisticsDialog())); + addCheckableActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::ShowRealtimeEntityStats); // Developer > Network >>> diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp new file mode 100644 index 0000000000..9c6fdcea8f --- /dev/null +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -0,0 +1,561 @@ +// +// OctreeStatsProvider.cpp +// interface/src/ui +// +// Created by Vlad Stelmahovsky on 3/12/17. +// Copyright 2013 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 + +#include +#include + +#include "Application.h" + +#include "../octree/OctreePacketProcessor.h" +#include "ui/OctreeStatsProvider.h" + +OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) : + QObject(parent), + _model(model) + //, _averageUpdatesPerSecond(SAMPLES_PER_SECOND) +{ + + _statCount = 0; +// _octreeServerLabelsCount = 0; + +// for (int i = 0; i < MAX_VOXEL_SERVERS; i++) { +// _octreeServerLables[i] = 0; +// } + + // Setup stat items +// _serverElements = AddStatItem("Elements on Servers"); + _localElements = AddStatItem("Local Elements"); + _localElementsMemory = AddStatItem("Elements Memory"); + _sendingMode = AddStatItem("Sending Mode"); + + _processedPackets = AddStatItem("Incoming Entity Packets"); + _processedPacketsElements = AddStatItem("Processed Packets Elements"); + _processedPacketsEntities = AddStatItem("Processed Packets Entities"); + _processedPacketsTiming = AddStatItem("Processed Packets Timing"); + + _outboundEditPackets = AddStatItem("Outbound Entity Packets"); + + _entityUpdateTime = AddStatItem("Entity Update Time"); + _entityUpdates = AddStatItem("Entity Updates"); + + //schedule updates + connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData); + _updateTimer.setInterval(100); + //timer will be rescheduled on each new timeout + _updateTimer.setSingleShot(true); + +} + +void OctreeStatsProvider::moreless(const QString& link) { + QStringList linkDetails = link.split("-"); + const int COMMAND_ITEM = 0; + const int SERVER_NUMBER_ITEM = 1; + QString serverNumberString = linkDetails[SERVER_NUMBER_ITEM]; + QString command = linkDetails[COMMAND_ITEM]; + int serverNumber = serverNumberString.toInt(); + +// if (command == "more") { +// _extraServerDetails[serverNumber-1] = MORE; +// } else if (command == "most") { +// _extraServerDetails[serverNumber-1] = MOST; +// } else { +// _extraServerDetails[serverNumber-1] = LESS; +// } +} + +/* + * Start updates statistics +*/ +void OctreeStatsProvider::startUpdates() { + _updateTimer.start(); +} + +/* + * Stop updates statistics +*/ +void OctreeStatsProvider::stopUpdates() { + _updateTimer.stop(); +} + + +int OctreeStatsProvider::AddStatItem(const char* caption, unsigned colorRGBA) { + const int STATS_LABEL_WIDTH = 600; + + _statCount++; // increment our current stat count + + if (colorRGBA == 0) { + static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; + colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; + } + //QLabel* label = _labels[_statCount] = new QLabel(); + + // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) + //QPalette palette = label->palette(); + + // This goofiness came from the bandwidth meter code, it basically stores a color in an unsigned and extracts it + unsigned rgb = colorRGBA >> 8; + const unsigned colorpart1 = 0xfefefeu; + const unsigned colorpart2 = 0xf8f8f8; + rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3); +// palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb)); +// label->setPalette(palette); +// _form->addRow(QString(" %1:").arg(caption), label); +// label->setFixedWidth(STATS_LABEL_WIDTH); + + return _statCount; +} + +OctreeStatsProvider::~OctreeStatsProvider() { + _updateTimer.stop(); +} + +int OctreeStatsProvider::serversNum() const { + return m_serversNum; +} + +void OctreeStatsProvider::updateOctreeStatsData() { + + // Processed Entities Related stats + auto entities = qApp->getEntities(); + auto entitiesTree = entities->getTree(); + + // Do this ever paint event... even if we don't update + auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits(); + + + // Only refresh our stats every once in a while, unless asked for realtime + //if no realtime, then update once per second. Otherwise consider 60FPS update, ie 16ms interval + int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000; + _updateTimer.start(updateinterval); + + const int FLOATING_POINT_PRECISION = 3; + + // Update labels + + //QLabel* label; + QLocale locale(QLocale::English); + std::stringstream statsValue; +// statsValue.precision(4); + + // Octree Elements Memory Usage + //label = _labels[_localElementsMemory]; +// statsValue.str(""); +// statsValue << "Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.0f << "MB "; + //label->setText(statsValue.str().c_str()); + m_localElementsMemory = QString("Elements RAM: %1MB").arg(OctreeElement::getTotalMemoryUsage() / 1000000.0f, 5, 'f', 4); + emit localElementsMemoryChanged(m_localElementsMemory); + // Local Elements + //label = _labels[_localElements]; +// unsigned long localTotal = OctreeElement::getNodeCount(); +// unsigned long localInternal = OctreeElement::getInternalNodeCount(); +// unsigned long localLeaves = OctreeElement::getLeafNodeCount(); +// QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); +// QString localInternalString = locale.toString((uint)localInternal); +// QString localLeavesString = locale.toString((uint)localLeaves); + +// statsValue.str(""); +// statsValue << +// "Total: " << qPrintable(localTotalString) << " / " << +// "Internal: " << qPrintable(localInternalString) << " / " << +// "Leaves: " << qPrintable(localLeavesString) << ""; + + m_localElements = QString("Total: %1 / Internal: %2 / Leaves: %3"). + arg(OctreeElement::getNodeCount()). + arg(OctreeElement::getInternalNodeCount()). + arg(OctreeElement::getLeafNodeCount()); + emit localElementsChanged(m_localElements); + + //label->setText(statsValue.str().c_str()); + + // iterate all the current octree stats, and list their sending modes, total their octree elements, etc... + std::stringstream sendingMode(""); + + int serverCount = 0; + int movingServerCount = 0; + unsigned long totalNodes = 0; + unsigned long totalInternal = 0; + unsigned long totalLeaves = 0; + + NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); + sceneStats->withReadLock([&] { + for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { + //const QUuid& uuid = i->first; + OctreeSceneStats& stats = i->second; + serverCount++; + + // calculate server node totals + totalNodes += stats.getTotalElements(); + totalInternal += stats.getTotalInternal(); + totalLeaves += stats.getTotalLeaves(); + + // Sending mode + if (serverCount > 1) { + sendingMode << ","; + } + if (stats.isMoving()) { + sendingMode << "M"; + movingServerCount++; + } else { + sendingMode << "S"; + } + if (stats.isFullScene()) { + sendingMode << "F"; + } else { + sendingMode << "p"; + } + } + }); + sendingMode << " - " << serverCount << " servers"; + if (movingServerCount > 0) { + sendingMode << " "; + } else { + sendingMode << " "; + } + +// label = _labels[_sendingMode]; +// label->setText(sendingMode.str().c_str()); + + // Server Elements + m_serverElements = QString("Total: %1/ Internal: %2/ Leaves: %3"). + arg(totalNodes).arg(totalInternal).arg(totalLeaves); + emit serverElementsChanged(m_serverElements); +// QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); +// QString serversInternalString = locale.toString((uint)totalInternal); +// QString serversLeavesString = locale.toString((uint)totalLeaves); +//// label = _labels[_serverElements]; +// statsValue.str(""); +// statsValue << +// "Total: " << qPrintable(serversTotalString) << " / " << +// "Internal: " << qPrintable(serversInternalString) << " / " << +// "Leaves: " << qPrintable(serversLeavesString) << ""; +//// label->setText(statsValue.str().c_str()); + + // Processed Packets Elements + auto averageElementsPerPacket = entities->getAverageElementsPerPacket(); + auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket(); + + auto averageElementsPerSecond = entities->getAverageElementsPerSecond(); + auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond(); + + auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket(); + auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket(); + auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket(); + + QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION); + QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION); + + QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION); + QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION); + + QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket); + QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket); + QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket); + +// label = _labels[_processedPackets]; + const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor(); + + auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount(); + auto incomingPPS = entitiesPacketProcessor.getIncomingPPS(); + auto processedPPS = entitiesPacketProcessor.getProcessedPPS(); + auto treeProcessedPPS = entities->getAveragePacketsPerSecond(); + + QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION); + QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION); + QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION); + + statsValue.str(""); + statsValue << + "Queue Size: " << incomingPacketsDepth << " Packets / " << + "Network IN: " << qPrintable(incomingPPSString) << " PPS / " << + "Queue OUT: " << qPrintable(processedPPSString) << " PPS / " << + "Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS"; + +// label->setText(statsValue.str().c_str()); + +// label = _labels[_processedPacketsElements]; + statsValue.str(""); + statsValue << + "" << qPrintable(averageElementsPerPacketString) << " per packet / " << + "" << qPrintable(averageElementsPerSecondString) << " per second"; + +// label->setText(statsValue.str().c_str()); + +// label = _labels[_processedPacketsEntities]; + statsValue.str(""); + statsValue << + "" << qPrintable(averageEntitiesPerPacketString) << " per packet / " << + "" << qPrintable(averageEntitiesPerSecondString) << " per second"; + +// label->setText(statsValue.str().c_str()); + +// label = _labels[_processedPacketsTiming]; + statsValue.str(""); + statsValue << + "Lock Wait: " << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " << + "Uncompress: " << qPrintable(averageUncompressPerPacketString) << " (usecs) / " << + "Process: " << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)"; + + //label->setText(statsValue.str().c_str()); + + auto entitiesEditPacketSender = qApp->getEntityEditPacketSender(); + + auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount(); + auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued(); + auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS(); + + QString outboundQueuedPPSString = locale.toString(outboundQueuedPPS, 'f', FLOATING_POINT_PRECISION); + QString outboundSentPPSString = locale.toString(outboundSentPPS, 'f', FLOATING_POINT_PRECISION); + + //label = _labels[_outboundEditPackets]; + statsValue.str(""); + statsValue << + "Queue Size: " << outboundPacketsDepth << " packets / " << + "Queued IN: " << qPrintable(outboundQueuedPPSString) << " PPS / " << + "Sent OUT: " << qPrintable(outboundSentPPSString) << " PPS"; + + //label->setText(statsValue.str().c_str()); + + + // Entity Edits update time + //label = _labels[_entityUpdateTime]; + auto averageEditDelta = entitiesTree->getAverageEditDeltas(); + auto maxEditDelta = entitiesTree->getMaxEditDelta(); + + QString averageEditDeltaString = locale.toString((uint)averageEditDelta); + QString maxEditDeltaString = locale.toString((uint)maxEditDelta); + + statsValue.str(""); + statsValue << + "Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " << + "Max: " << qPrintable(maxEditDeltaString) << " (usecs)"; + + //label->setText(statsValue.str().c_str()); + + // Entity Edits + //label = _labels[_entityUpdates]; + auto bytesPerEdit = entitiesTree->getAverageEditBytes(); + + auto updatesPerSecond = _averageUpdatesPerSecond.getAverage(); + if (updatesPerSecond < 1) { + updatesPerSecond = 0; // we don't really care about small updates per second so suppress those + } + + QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits); + QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION); + QString bytesPerEditString = locale.toString(bytesPerEdit); + + statsValue.str(""); + statsValue << + "" << qPrintable(updatesPerSecondString) << " updates per second / " << + "" << qPrintable(totalTrackedEditsString) << " total updates / " << + "Average Size: " << qPrintable(bytesPerEditString) << " bytes "; + + //label->setText(statsValue.str().c_str()); + + updateOctreeServers(); +} + +void OctreeStatsProvider::updateOctreeServers() { + int serverCount = 0; + + showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity", + qApp->getEntityServerJurisdictions()); + //qDebug() << "vladest: octree servers:" << serverCount; + if (m_serversNum != serverCount) { + m_serversNum = serverCount; + emit serversNumChanged(m_serversNum); + } +} + +void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName, + NodeToJurisdictionMap& serverJurisdictions) { + + QLocale locale(QLocale::English); + + auto nodeList = DependencyManager::get(); + nodeList->eachNode([&](const SharedNodePointer& node){ + + // only send to the NodeTypes that are NodeType_t_VOXEL_SERVER + if (node->getType() == serverType) { + serverCount++; + +// if (serverCount > _octreeServerLabelsCount) { +// QString label = QString("%1 Server %2").arg(serverTypeName).arg(serverCount); +// //int thisServerRow = _octreeServerLables[serverCount-1] = AddStatItem(label.toUtf8().constData()); +//// _labels[thisServerRow]->setTextFormat(Qt::RichText); +//// _labels[thisServerRow]->setTextInteractionFlags(Qt::TextBrowserInteraction); +//// connect(_labels[thisServerRow], SIGNAL(linkActivated(const QString&)), this, SLOT(moreless(const QString&))); +// _octreeServerLabelsCount++; +// } + + std::stringstream serverDetails(""); + std::stringstream extraDetails(""); + std::stringstream linkDetails(""); + + if (node->getActiveSocket()) { + serverDetails << "active "; + } else { + serverDetails << "inactive "; + } + + QUuid nodeUUID = node->getUUID(); + + // lookup our nodeUUID in the jurisdiction map, if it's missing then we're + // missing at least one jurisdiction + serverJurisdictions.withReadLock([&] { + if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) { + serverDetails << " unknown jurisdiction "; + return; + } + const JurisdictionMap& map = serverJurisdictions[nodeUUID]; + + auto rootCode = map.getRootOctalCode(); + + if (rootCode) { + QString rootCodeHex = octalCodeToHexString(rootCode.get()); + + VoxelPositionSize rootDetails; + voxelDetailsForCode(rootCode.get(), rootDetails); + AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); + serverDetails << " jurisdiction: " + << qPrintable(rootCodeHex) + << " [" + << rootDetails.x << ", " + << rootDetails.y << ", " + << rootDetails.z << ": " + << rootDetails.s << "] "; + } else { + serverDetails << " jurisdiction has no rootCode"; + } // root code + }); + + // now lookup stats details for this server... + if (/*_extraServerDetails[serverCount-1] != LESS*/true) { + NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); + sceneStats->withReadLock([&] { + if (sceneStats->find(nodeUUID) != sceneStats->end()) { + OctreeSceneStats& stats = sceneStats->at(nodeUUID); +/* + switch (_extraServerDetails[serverCount - 1]) { + case MOST: { + extraDetails << "
"; + + float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; + float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; + } + + QString lastFullEncodeString = locale.toString(lastFullEncode); + QString lastFullSendString = locale.toString(lastFullSend); + QString lastFullPacketsString = locale.toString(lastFullPackets); + QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); + QString lastFullPPSString = locale.toString(lastFullPPS); + + extraDetails << "
" << "Last Full Scene... " << + "Encode: " << qPrintable(lastFullEncodeString) << " ms " << + "Send: " << qPrintable(lastFullSendString) << " ms " << + "Packets: " << qPrintable(lastFullPacketsString) << " " << + "Bytes: " << qPrintable(lastFullBytesString) << " " << + "Rate: " << qPrintable(lastFullPPSString) << " PPS"; + + for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { + OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); + OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); + extraDetails << "
" << itemInfo.caption << " " << stats.getItemValue(item); + } + } // fall through... since MOST has all of MORE + case MORE: { + QString totalString = locale.toString((uint)stats.getTotalElements()); + QString internalString = locale.toString((uint)stats.getTotalInternal()); + QString leavesString = locale.toString((uint)stats.getTotalLeaves()); + + serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; + + serverDetails << "
" << "Elements: " << + qPrintable(totalString) << " total " << + qPrintable(internalString) << " internal " << + qPrintable(leavesString) << " leaves "; + + QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); + QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); + QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); + const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); + QString incomingOutOfOrderString = locale.toString((uint)seqStats.getOutOfOrder()); + QString incomingLateString = locale.toString((uint)seqStats.getLate()); + QString incomingUnreasonableString = locale.toString((uint)seqStats.getUnreasonable()); + QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); + QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); + QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); + + qint64 clockSkewInUsecs = node->getClockSkewUsec(); + QString formattedClockSkewString = formatUsecTime(clockSkewInUsecs); + qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; + QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); + QString incomingPingTimeString = locale.toString(node->getPingMs()); + QString incomingClockSkewString = locale.toString(clockSkewInMS); + + serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << + "/ Lost: " << qPrintable(incomingLikelyLostString) << + "/ Recovered: " << qPrintable(incomingRecovered); + + serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << + "/ Early: " << qPrintable(incomingEarlyString) << + "/ Late: " << qPrintable(incomingLateString) << + "/ Unreasonable: " << qPrintable(incomingUnreasonableString); + + serverDetails << "
" << + " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; + + serverDetails << "
" << + " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; + + serverDetails << "
" << + " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" << + " [" << qPrintable(formattedClockSkewString) << "]"; + + + serverDetails << "
" << "Incoming" << + " Bytes: " << qPrintable(incomingBytesString) << + " Wasted Bytes: " << qPrintable(incomingWastedBytesString); + + serverDetails << extraDetails.str(); + if (_extraServerDetails[serverCount - 1] == MORE) { + linkDetails << " " << " [most...]"; + linkDetails << " " << " [less...]"; + } else { + linkDetails << " " << " [less...]"; + linkDetails << " " << " [least...]"; + } + + } break; + case LESS: { + // nothing + } break; + }*/ + } + }); + } else { + linkDetails << " " << " [more...]"; + linkDetails << " " << " [most...]"; + } + serverDetails << linkDetails.str(); + //_labels[_octreeServerLables[serverCount - 1]]->setText(serverDetails.str().c_str()); + } // is VOXEL_SERVER + }); +} + + diff --git a/interface/src/ui/OctreeStatsProvider.h b/interface/src/ui/OctreeStatsProvider.h new file mode 100644 index 0000000000..c2fc46218d --- /dev/null +++ b/interface/src/ui/OctreeStatsProvider.h @@ -0,0 +1,128 @@ +// +// OctreeStatsProvider.h +// interface/src/ui +// +// Created by Vlad Stelmahovsky on 3/12/17. +// Copyright 2017 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_OctreeStatsProvider_h +#define hifi_OctreeStatsProvider_h + +#include +#include + +#include "DependencyManager.h" + +#define MAX_STATS 100 +#define MAX_VOXEL_SERVERS 50 +#define DEFAULT_COLOR 0 + +class OctreeStatsProvider : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + Q_PROPERTY(int serversNum READ serversNum NOTIFY serversNumChanged) + Q_PROPERTY(QString serverElements READ serverElements NOTIFY serverElementsChanged) + Q_PROPERTY(QString localElements READ localElements NOTIFY localElementsChanged) + Q_PROPERTY(QString localElementsMemory READ localElementsMemory NOTIFY localElementsMemoryChanged) + Q_PROPERTY(QString sendingMode READ sendingMode NOTIFY sendingModeChanged) + //Incoming Entity Packets + Q_PROPERTY(QString processedPackets READ processedPackets NOTIFY processedPacketsChanged) + +// int _entityUpdateTime; +// int _entityUpdates; +// int _processedPacketsElements; +// int _processedPacketsEntities; +// int _processedPacketsTiming; +// int _outboundEditPackets; + +public: + // Sets up the UI + OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model); + ~OctreeStatsProvider(); + + int serversNum() const; + + QString serverElements() const { + return m_serverElements; + } + + QString localElements() const { + return m_localElements; + } + + QString localElementsMemory() const { + return m_localElementsMemory; + } + + QString sendingMode() const { + return m_sendingMode; + } + + QString processedPackets() const { + return m_processedPackets; + } + +signals: + + void serversNumChanged(int serversNum); + void serverElementsChanged(QString serverElements); + void localElementsChanged(QString localElements); + void sendingModeChanged(QString sendingMode); + void processedPacketsChanged(QString processedPackets); + void localElementsMemoryChanged(QString localElementsMemory); + +public slots: + void moreless(const QString& link); + void startUpdates(); + void stopUpdates(); + +private slots: + void updateOctreeStatsData(); +protected: + + int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR); + void updateOctreeServers(); + void showOctreeServersOfType(int& serverNumber, NodeType_t serverType, + const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions); + +private: + NodeToOctreeSceneStats* _model; + int _statCount; + + int _sendingMode; + int _serverElements; + int _localElements; + int _localElementsMemory; + + int _entityUpdateTime; + int _entityUpdates; + int _processedPackets; + int _processedPacketsElements; + int _processedPacketsEntities; + int _processedPacketsTiming; + int _outboundEditPackets; + + const int SAMPLES_PER_SECOND = 10; + SimpleMovingAverage _averageUpdatesPerSecond; + quint64 _lastWindowAt = usecTimestampNow(); + quint64 _lastKnownTrackedEdits = 0; + + quint64 _lastRefresh = 0; + +// int _octreeServerLables[MAX_VOXEL_SERVERS]; +// int _octreeServerLabelsCount; + QTimer _updateTimer; + int m_serversNum {0}; + QString m_serverElements; + QString m_localElements; + QString m_localElementsMemory; + QString m_sendingMode; + QString m_processedPackets; +}; + +#endif // hifi_OctreeStatsProvider_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 32fe26a697..2aa6725aa2 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -43,6 +43,7 @@ #include "FileDialogHelper.h" #include "avatar/AvatarManager.h" #include "AudioClient.h" +#include "ui/OctreeStatsProvider.h" static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -182,6 +183,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("pathToFonts", "../../"); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); From b67b17d3c01bbdfb957a8feba6506cbcea02b14b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 13 Mar 2017 09:10:34 -0800 Subject: [PATCH 19/32] Fix JS exception messages formatting --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73a79f1bc6..d721d1c86f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -142,7 +142,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) QString ScriptEngine::logException(const QScriptValue& exception) { auto message = formatException(exception); - scriptErrorMessage(qPrintable(message)); + scriptErrorMessage(message); return message; } @@ -453,7 +453,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { } void ScriptEngine::scriptErrorMessage(const QString& message) { - qCCritical(scriptengine) << message; + qCCritical(scriptengine) << qPrintable(message); emit errorMessage(message); } From 48db6201e5f3aa7f261ae98d2ba401fe77e9a1f8 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Mon, 13 Mar 2017 19:48:50 +0100 Subject: [PATCH 20/32] Basic statistics showed. TODO: servers statistics, coloring, wrapping --- .../hifi/dialogs/TabletEntityStatistics.qml | 28 +- interface/src/ui/OctreeStatsProvider.cpp | 260 ++++++------------ interface/src/ui/OctreeStatsProvider.h | 78 +++--- 3 files changed, 148 insertions(+), 218 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index 21f63dd070..bd7796927e 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -64,7 +64,7 @@ Rectangle { } HifiControls.Label { id: elementsOnServerLabel - size: 20 + size: 16 text: OctreeStats.serverElements anchors.left: parent.left anchors.right: parent.right @@ -80,7 +80,7 @@ Rectangle { } HifiControls.Label { id: localElementsLabel - size: 20 + size: 16 text: OctreeStats.localElements anchors.left: parent.left anchors.right: parent.right @@ -96,7 +96,7 @@ Rectangle { } HifiControls.Label { id: elementsMemoryLabel - size: 20 + size: 16 text: OctreeStats.localElementsMemory anchors.left: parent.left anchors.right: parent.right @@ -112,7 +112,7 @@ Rectangle { } HifiControls.Label { id: sendingModeLabel - size: 20 + size: 16 text: OctreeStats.sendingMode anchors.left: parent.left anchors.right: parent.right @@ -128,7 +128,7 @@ Rectangle { } HifiControls.Label { id: incomingEntityPacketsLabel - size: 20 + size: 16 text: OctreeStats.processedPackets anchors.left: parent.left anchors.right: parent.right @@ -144,7 +144,8 @@ Rectangle { } HifiControls.Label { id: processedPacketsElementsLabel - size: 20 + size: 16 + text: OctreeStats.processedPacketsElements anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme @@ -159,7 +160,8 @@ Rectangle { } HifiControls.Label { id: processedPacketsEntitiesLabel - size: 20 + size: 16 + text: OctreeStats.processedPacketsEntities anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme @@ -174,7 +176,8 @@ Rectangle { } HifiControls.Label { id: processedPacketsTimingLabel - size: 20 + size: 16 + text: OctreeStats.processedPacketsTiming anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme @@ -189,7 +192,8 @@ Rectangle { } HifiControls.Label { id: outboundEntityPacketsLabel - size: 20 + size: 16 + text: OctreeStats.outboundEditPackets anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme @@ -204,7 +208,8 @@ Rectangle { } HifiControls.Label { id: entityUpdateTimeLabel - size: 20 + size: 16 + text: OctreeStats.entityUpdateTime anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme @@ -219,7 +224,8 @@ Rectangle { } HifiControls.Label { id: entityUpdatesLabel - size: 20 + size: 16 + text: OctreeStats.entityUpdates anchors.left: parent.left anchors.right: parent.right colorScheme: root.colorScheme diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index 9c6fdcea8f..057056d8ce 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -22,38 +22,14 @@ OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) : QObject(parent), _model(model) - //, _averageUpdatesPerSecond(SAMPLES_PER_SECOND) + , _averageUpdatesPerSecond(SAMPLES_PER_SECOND) + , _statCount(0) { - - _statCount = 0; -// _octreeServerLabelsCount = 0; - -// for (int i = 0; i < MAX_VOXEL_SERVERS; i++) { -// _octreeServerLables[i] = 0; -// } - - // Setup stat items -// _serverElements = AddStatItem("Elements on Servers"); - _localElements = AddStatItem("Local Elements"); - _localElementsMemory = AddStatItem("Elements Memory"); - _sendingMode = AddStatItem("Sending Mode"); - - _processedPackets = AddStatItem("Incoming Entity Packets"); - _processedPacketsElements = AddStatItem("Processed Packets Elements"); - _processedPacketsEntities = AddStatItem("Processed Packets Entities"); - _processedPacketsTiming = AddStatItem("Processed Packets Timing"); - - _outboundEditPackets = AddStatItem("Outbound Entity Packets"); - - _entityUpdateTime = AddStatItem("Entity Update Time"); - _entityUpdates = AddStatItem("Entity Updates"); - //schedule updates connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData); _updateTimer.setInterval(100); //timer will be rescheduled on each new timeout _updateTimer.setSingleShot(true); - } void OctreeStatsProvider::moreless(const QString& link) { @@ -88,32 +64,32 @@ void OctreeStatsProvider::stopUpdates() { } -int OctreeStatsProvider::AddStatItem(const char* caption, unsigned colorRGBA) { - const int STATS_LABEL_WIDTH = 600; +//int OctreeStatsProvider::AddStatItem(const char* caption, unsigned colorRGBA) { +// const int STATS_LABEL_WIDTH = 600; - _statCount++; // increment our current stat count +// _statCount++; // increment our current stat count - if (colorRGBA == 0) { - static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; - colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; - } - //QLabel* label = _labels[_statCount] = new QLabel(); +// if (colorRGBA == 0) { +// static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; +// colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; +// } +// //QLabel* label = _labels[_statCount] = new QLabel(); - // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) - //QPalette palette = label->palette(); +// // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) +// //QPalette palette = label->palette(); - // This goofiness came from the bandwidth meter code, it basically stores a color in an unsigned and extracts it - unsigned rgb = colorRGBA >> 8; - const unsigned colorpart1 = 0xfefefeu; - const unsigned colorpart2 = 0xf8f8f8; - rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3); +// // This goofiness came from the bandwidth meter code, it basically stores a color in an unsigned and extracts it +// unsigned rgb = colorRGBA >> 8; +// const unsigned colorpart1 = 0xfefefeu; +// const unsigned colorpart2 = 0xf8f8f8; +// rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3); // palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb)); // label->setPalette(palette); // _form->addRow(QString(" %1:").arg(caption), label); // label->setFixedWidth(STATS_LABEL_WIDTH); - return _statCount; -} +// return _statCount; +//} OctreeStatsProvider::~OctreeStatsProvider() { _updateTimer.stop(); @@ -132,54 +108,43 @@ void OctreeStatsProvider::updateOctreeStatsData() { // Do this ever paint event... even if we don't update auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits(); + const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND; + quint64 now = usecTimestampNow(); + quint64 sinceLastWindow = now - _lastWindowAt; + auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits; + float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND; + float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds; + if (sinceLastWindow > SAMPLING_WINDOW) { + _averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond); + _lastWindowAt = now; + _lastKnownTrackedEdits = totalTrackedEdits; + } + // Only refresh our stats every once in a while, unless asked for realtime + quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND; + quint64 sinceLastRefresh = now - _lastRefresh; + if (sinceLastRefresh < REFRESH_AFTER) { + _updateTimer.start((REFRESH_AFTER - sinceLastRefresh)/1000); + return; + } // Only refresh our stats every once in a while, unless asked for realtime //if no realtime, then update once per second. Otherwise consider 60FPS update, ie 16ms interval - int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000; - _updateTimer.start(updateinterval); + //int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000; + _updateTimer.start(SAMPLING_WINDOW/1000); const int FLOATING_POINT_PRECISION = 3; - // Update labels - - //QLabel* label; - QLocale locale(QLocale::English); - std::stringstream statsValue; -// statsValue.precision(4); - - // Octree Elements Memory Usage - //label = _labels[_localElementsMemory]; -// statsValue.str(""); -// statsValue << "Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.0f << "MB "; - //label->setText(statsValue.str().c_str()); m_localElementsMemory = QString("Elements RAM: %1MB").arg(OctreeElement::getTotalMemoryUsage() / 1000000.0f, 5, 'f', 4); emit localElementsMemoryChanged(m_localElementsMemory); + // Local Elements - //label = _labels[_localElements]; -// unsigned long localTotal = OctreeElement::getNodeCount(); -// unsigned long localInternal = OctreeElement::getInternalNodeCount(); -// unsigned long localLeaves = OctreeElement::getLeafNodeCount(); -// QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); -// QString localInternalString = locale.toString((uint)localInternal); -// QString localLeavesString = locale.toString((uint)localLeaves); - -// statsValue.str(""); -// statsValue << -// "Total: " << qPrintable(localTotalString) << " / " << -// "Internal: " << qPrintable(localInternalString) << " / " << -// "Leaves: " << qPrintable(localLeavesString) << ""; - m_localElements = QString("Total: %1 / Internal: %2 / Leaves: %3"). arg(OctreeElement::getNodeCount()). arg(OctreeElement::getInternalNodeCount()). arg(OctreeElement::getLeafNodeCount()); emit localElementsChanged(m_localElements); - //label->setText(statsValue.str().c_str()); - // iterate all the current octree stats, and list their sending modes, total their octree elements, etc... - std::stringstream sendingMode(""); - int serverCount = 0; int movingServerCount = 0; unsigned long totalNodes = 0; @@ -200,45 +165,35 @@ void OctreeStatsProvider::updateOctreeStatsData() { // Sending mode if (serverCount > 1) { - sendingMode << ","; + m_sendingMode += ","; } if (stats.isMoving()) { - sendingMode << "M"; + m_sendingMode += "M"; movingServerCount++; } else { - sendingMode << "S"; + m_sendingMode += "S"; } if (stats.isFullScene()) { - sendingMode << "F"; + m_sendingMode += "F"; } else { - sendingMode << "p"; + m_sendingMode += "p"; } } }); - sendingMode << " - " << serverCount << " servers"; + m_sendingMode += QString(" - %1 servers").arg(serverCount); if (movingServerCount > 0) { - sendingMode << " "; + m_sendingMode += " "; } else { - sendingMode << " "; + m_sendingMode += " "; } -// label = _labels[_sendingMode]; -// label->setText(sendingMode.str().c_str()); + emit sendingModeChanged(m_sendingMode); // Server Elements - m_serverElements = QString("Total: %1/ Internal: %2/ Leaves: %3"). + m_serverElements = QString("Total: %1 / Internal: %2 / Leaves: %3"). arg(totalNodes).arg(totalInternal).arg(totalLeaves); emit serverElementsChanged(m_serverElements); -// QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); -// QString serversInternalString = locale.toString((uint)totalInternal); -// QString serversLeavesString = locale.toString((uint)totalLeaves); -//// label = _labels[_serverElements]; -// statsValue.str(""); -// statsValue << -// "Total: " << qPrintable(serversTotalString) << " / " << -// "Internal: " << qPrintable(serversInternalString) << " / " << -// "Leaves: " << qPrintable(serversLeavesString) << ""; -//// label->setText(statsValue.str().c_str()); + // Processed Packets Elements auto averageElementsPerPacket = entities->getAverageElementsPerPacket(); @@ -251,17 +206,6 @@ void OctreeStatsProvider::updateOctreeStatsData() { auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket(); auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket(); - QString averageElementsPerPacketString = locale.toString(averageElementsPerPacket, 'f', FLOATING_POINT_PRECISION); - QString averageEntitiesPerPacketString = locale.toString(averageEntitiesPerPacket, 'f', FLOATING_POINT_PRECISION); - - QString averageElementsPerSecondString = locale.toString(averageElementsPerSecond, 'f', FLOATING_POINT_PRECISION); - QString averageEntitiesPerSecondString = locale.toString(averageEntitiesPerSecond, 'f', FLOATING_POINT_PRECISION); - - QString averageWaitLockPerPacketString = locale.toString(averageWaitLockPerPacket); - QString averageUncompressPerPacketString = locale.toString(averageUncompressPerPacket); - QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket); - -// label = _labels[_processedPackets]; const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor(); auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount(); @@ -269,80 +213,50 @@ void OctreeStatsProvider::updateOctreeStatsData() { auto processedPPS = entitiesPacketProcessor.getProcessedPPS(); auto treeProcessedPPS = entities->getAveragePacketsPerSecond(); - QString incomingPPSString = locale.toString(incomingPPS, 'f', FLOATING_POINT_PRECISION); - QString processedPPSString = locale.toString(processedPPS, 'f', FLOATING_POINT_PRECISION); - QString treeProcessedPPSString = locale.toString(treeProcessedPPS, 'f', FLOATING_POINT_PRECISION); + m_processedPackets = QString("Queue Size: %1 Packets / Network IN: %2 PPS / Queue OUT: %3 PPS / Tree IN: %4 PPS") + .arg(incomingPacketsDepth) + .arg(incomingPPS, 5, 'f', FLOATING_POINT_PRECISION) + .arg(processedPPS, 5, 'f', FLOATING_POINT_PRECISION) + .arg(treeProcessedPPS, 5, 'f', FLOATING_POINT_PRECISION); + emit processedPacketsChanged(m_processedPackets); - statsValue.str(""); - statsValue << - "Queue Size: " << incomingPacketsDepth << " Packets / " << - "Network IN: " << qPrintable(incomingPPSString) << " PPS / " << - "Queue OUT: " << qPrintable(processedPPSString) << " PPS / " << - "Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS"; - -// label->setText(statsValue.str().c_str()); + m_processedPacketsElements = QString("%1 per packet / %2 per second") + .arg(averageElementsPerPacket, 5, 'f', FLOATING_POINT_PRECISION) + .arg(averageElementsPerSecond, 5, 'f', FLOATING_POINT_PRECISION); + emit processedPacketsElementsChanged(m_processedPacketsElements); -// label = _labels[_processedPacketsElements]; - statsValue.str(""); - statsValue << - "" << qPrintable(averageElementsPerPacketString) << " per packet / " << - "" << qPrintable(averageElementsPerSecondString) << " per second"; - -// label->setText(statsValue.str().c_str()); + m_processedPacketsEntities = QString("%1 per packet / %2 per second") + .arg(averageEntitiesPerPacket, 5, 'f', FLOATING_POINT_PRECISION) + .arg(averageEntitiesPerSecond, 5, 'f', FLOATING_POINT_PRECISION); + emit processedPacketsEntitiesChanged(m_processedPacketsEntities); -// label = _labels[_processedPacketsEntities]; - statsValue.str(""); - statsValue << - "" << qPrintable(averageEntitiesPerPacketString) << " per packet / " << - "" << qPrintable(averageEntitiesPerSecondString) << " per second"; - -// label->setText(statsValue.str().c_str()); - -// label = _labels[_processedPacketsTiming]; - statsValue.str(""); - statsValue << - "Lock Wait: " << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " << - "Uncompress: " << qPrintable(averageUncompressPerPacketString) << " (usecs) / " << - "Process: " << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)"; - - //label->setText(statsValue.str().c_str()); + m_processedPacketsTiming = QString("Lock Wait: %1 (usecs) / Uncompress: %2 (usecs) / Process: %3 (usecs)") + .arg(averageWaitLockPerPacket) + .arg(averageUncompressPerPacket) + .arg(averageReadBitstreamPerPacket); + emit processedPacketsTimingChanged(m_processedPacketsTiming); auto entitiesEditPacketSender = qApp->getEntityEditPacketSender(); - auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount(); auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued(); auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS(); - QString outboundQueuedPPSString = locale.toString(outboundQueuedPPS, 'f', FLOATING_POINT_PRECISION); - QString outboundSentPPSString = locale.toString(outboundSentPPS, 'f', FLOATING_POINT_PRECISION); - - //label = _labels[_outboundEditPackets]; - statsValue.str(""); - statsValue << - "Queue Size: " << outboundPacketsDepth << " packets / " << - "Queued IN: " << qPrintable(outboundQueuedPPSString) << " PPS / " << - "Sent OUT: " << qPrintable(outboundSentPPSString) << " PPS"; - - //label->setText(statsValue.str().c_str()); - + m_outboundEditPackets = QString("Queue Size: %1 packets / Queued IN: %2 PPS / Sent OUT: %3 PPS") + .arg(outboundPacketsDepth) + .arg(outboundQueuedPPS, 5, 'f', FLOATING_POINT_PRECISION) + .arg(outboundSentPPS, 5, 'f', FLOATING_POINT_PRECISION); + emit outboundEditPacketsChanged(m_outboundEditPackets); // Entity Edits update time - //label = _labels[_entityUpdateTime]; auto averageEditDelta = entitiesTree->getAverageEditDeltas(); auto maxEditDelta = entitiesTree->getMaxEditDelta(); - QString averageEditDeltaString = locale.toString((uint)averageEditDelta); - QString maxEditDeltaString = locale.toString((uint)maxEditDelta); - - statsValue.str(""); - statsValue << - "Average: " << qPrintable(averageEditDeltaString) << " (usecs) / " << - "Max: " << qPrintable(maxEditDeltaString) << " (usecs)"; - - //label->setText(statsValue.str().c_str()); + m_entityUpdateTime = QString("Average: %1 (usecs) / Max: %2 (usecs)") + .arg(averageEditDelta) + .arg(maxEditDelta); + emit entityUpdateTimeChanged(m_entityUpdateTime); // Entity Edits - //label = _labels[_entityUpdates]; auto bytesPerEdit = entitiesTree->getAverageEditBytes(); auto updatesPerSecond = _averageUpdatesPerSecond.getAverage(); @@ -350,17 +264,11 @@ void OctreeStatsProvider::updateOctreeStatsData() { updatesPerSecond = 0; // we don't really care about small updates per second so suppress those } - QString totalTrackedEditsString = locale.toString((uint)totalTrackedEdits); - QString updatesPerSecondString = locale.toString(updatesPerSecond, 'f', FLOATING_POINT_PRECISION); - QString bytesPerEditString = locale.toString(bytesPerEdit); - - statsValue.str(""); - statsValue << - "" << qPrintable(updatesPerSecondString) << " updates per second / " << - "" << qPrintable(totalTrackedEditsString) << " total updates / " << - "Average Size: " << qPrintable(bytesPerEditString) << " bytes "; - - //label->setText(statsValue.str().c_str()); + m_entityUpdates = QString("%1 updates per second / %2 total updates / Average Size: %3 bytes") + .arg(updatesPerSecond, 5, 'f', FLOATING_POINT_PRECISION) + .arg(totalTrackedEdits) + .arg(bytesPerEdit); + emit entityUpdatesChanged(m_entityUpdates); updateOctreeServers(); } diff --git a/interface/src/ui/OctreeStatsProvider.h b/interface/src/ui/OctreeStatsProvider.h index c2fc46218d..6fc3e091f4 100644 --- a/interface/src/ui/OctreeStatsProvider.h +++ b/interface/src/ui/OctreeStatsProvider.h @@ -30,18 +30,15 @@ class OctreeStatsProvider : public QObject, public Dependency { Q_PROPERTY(QString localElements READ localElements NOTIFY localElementsChanged) Q_PROPERTY(QString localElementsMemory READ localElementsMemory NOTIFY localElementsMemoryChanged) Q_PROPERTY(QString sendingMode READ sendingMode NOTIFY sendingModeChanged) - //Incoming Entity Packets Q_PROPERTY(QString processedPackets READ processedPackets NOTIFY processedPacketsChanged) - -// int _entityUpdateTime; -// int _entityUpdates; -// int _processedPacketsElements; -// int _processedPacketsEntities; -// int _processedPacketsTiming; -// int _outboundEditPackets; + Q_PROPERTY(QString processedPacketsElements READ processedPacketsElements NOTIFY processedPacketsElementsChanged) + Q_PROPERTY(QString processedPacketsEntities READ processedPacketsEntities NOTIFY processedPacketsEntitiesChanged) + Q_PROPERTY(QString processedPacketsTiming READ processedPacketsTiming NOTIFY processedPacketsTimingChanged) + Q_PROPERTY(QString outboundEditPackets READ outboundEditPackets NOTIFY outboundEditPacketsChanged) + Q_PROPERTY(QString entityUpdateTime READ entityUpdateTime NOTIFY entityUpdateTimeChanged) + Q_PROPERTY(QString entityUpdates READ entityUpdates NOTIFY entityUpdatesChanged) public: - // Sets up the UI OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model); ~OctreeStatsProvider(); @@ -67,14 +64,44 @@ public: return m_processedPackets; } + QString processedPacketsElements() const { + return m_processedPacketsElements; + } + + QString processedPacketsEntities() const { + return m_processedPacketsEntities; + } + + QString processedPacketsTiming() const { + return m_processedPacketsTiming; + } + + QString outboundEditPackets() const { + return m_outboundEditPackets; + } + + QString entityUpdateTime() const { + return m_entityUpdateTime; + } + + QString entityUpdates() const { + return m_entityUpdates; + } + signals: void serversNumChanged(int serversNum); - void serverElementsChanged(QString serverElements); - void localElementsChanged(QString localElements); - void sendingModeChanged(QString sendingMode); - void processedPacketsChanged(QString processedPackets); - void localElementsMemoryChanged(QString localElementsMemory); + void serverElementsChanged(const QString &serverElements); + void localElementsChanged(const QString &localElements); + void sendingModeChanged(const QString &sendingMode); + void processedPacketsChanged(const QString &processedPackets); + void localElementsMemoryChanged(const QString &localElementsMemory); + void processedPacketsElementsChanged(const QString &processedPacketsElements); + void processedPacketsEntitiesChanged(const QString &processedPacketsEntities); + void processedPacketsTimingChanged(const QString &processedPacketsTiming); + void outboundEditPacketsChanged(const QString &outboundEditPackets); + void entityUpdateTimeChanged(const QString &entityUpdateTime); + void entityUpdatesChanged(const QString &entityUpdates); public slots: void moreless(const QString& link); @@ -84,8 +111,6 @@ public slots: private slots: void updateOctreeStatsData(); protected: - - int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR); void updateOctreeServers(); void showOctreeServersOfType(int& serverNumber, NodeType_t serverType, const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions); @@ -94,19 +119,6 @@ private: NodeToOctreeSceneStats* _model; int _statCount; - int _sendingMode; - int _serverElements; - int _localElements; - int _localElementsMemory; - - int _entityUpdateTime; - int _entityUpdates; - int _processedPackets; - int _processedPacketsElements; - int _processedPacketsEntities; - int _processedPacketsTiming; - int _outboundEditPackets; - const int SAMPLES_PER_SECOND = 10; SimpleMovingAverage _averageUpdatesPerSecond; quint64 _lastWindowAt = usecTimestampNow(); @@ -114,8 +126,6 @@ private: quint64 _lastRefresh = 0; -// int _octreeServerLables[MAX_VOXEL_SERVERS]; -// int _octreeServerLabelsCount; QTimer _updateTimer; int m_serversNum {0}; QString m_serverElements; @@ -123,6 +133,12 @@ private: QString m_localElementsMemory; QString m_sendingMode; QString m_processedPackets; + QString m_processedPacketsElements; + QString m_processedPacketsEntities; + QString m_processedPacketsTiming; + QString m_outboundEditPackets; + QString m_entityUpdateTime; + QString m_entityUpdates; }; #endif // hifi_OctreeStatsProvider_h From 3e020ede78ba0584114a0c402f4d93af0ae0fc32 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 13 Mar 2017 11:58:43 -0700 Subject: [PATCH 21/32] CR feedback --- libraries/audio-client/src/AudioClient.cpp | 9 +- libraries/audio-client/src/AudioNoiseGate.cpp | 97 ++++++++++--------- libraries/audio-client/src/AudioNoiseGate.h | 24 ++--- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 9fb8bae6ca..c32b5600d9 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1028,7 +1028,7 @@ void AudioClient::handleAudioInput() { // if we performed the noise gate we can get values from it instead of enumerating the samples again _lastInputLoudness = _inputGate.getLastLoudness(); - if (_inputGate.clippedInLastFrame()) { + if (_inputGate.clippedInLastBlock()) { _timeSinceLastClip = 0.0f; } @@ -1049,10 +1049,9 @@ void AudioClient::handleAudioInput() { emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); - if (_inputGate.openedInLastFrame()) { + if (_inputGate.openedInLastBlock()) { emit noiseGateOpened(); - } - if (_inputGate.closedInLastFrame()) { + } else if (_inputGate.closedInLastBlock()) { emit noiseGateClosed(); } @@ -1072,7 +1071,7 @@ void AudioClient::handleAudioInput() { // the output from the input gate (eventually, this could be crossfaded) // and allow the codec to properly encode down to silent/zero. If we still // have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet - if (_lastInputLoudness == 0 && !_inputGate.closedInLastFrame()) { + if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) { packetType = PacketType::SilentAudioFrame; _silentOutbound.increment(); } else { diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 17bbd1a509..2acdea6f0c 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -19,16 +19,16 @@ const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f; AudioNoiseGate::AudioNoiseGate() : - _inputFrameCounter(0), + _inputBlockCounter(0), _lastLoudness(0.0f), - _quietestFrame(std::numeric_limits::max()), - _loudestFrame(0.0f), - _didClipInLastFrame(false), + _quietestBlock(std::numeric_limits::max()), + _loudestBlock(0.0f), + _didClipInLastBlock(false), _dcOffset(0.0f), _measuredFloor(0.0f), _sampleCounter(0), _isOpen(false), - _framesToClose(0) + _buffersToClose(0) { } @@ -37,7 +37,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { // // DC Offset correction // - // Measure the DC offset over a trailing number of frames, and remove it from the input signal. + // Measure the DC offset over a trailing number of buffers, and remove it from the input signal. // This causes the noise background measurements and server muting to be more accurate. Many off-board // ADC's have a noticeable DC offset. // @@ -51,7 +51,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { // Update measured DC offset measuredDcOffset /= numSamples; if (_dcOffset == 0.0f) { - // On first frame, copy over measured offset + // On first buffer, copy over measured offset _dcOffset = measuredDcOffset; } else { _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset; @@ -69,13 +69,13 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { // // NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate. // Make this value lower for more sensitivity and less rejection of noise. - // NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded + // NOISE_GATE_WIDTH: The number of samples in an audio buffer for which the height must be exceeded // to open the gate. - // NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames + // NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the buffer, how many buffers // will we wait before closing the gate. - // NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor. + // NOISE_GATE_BLOCKSS_TO_AVERAGE: How many audio buffers should we average together to compute noise floor. // More means better rejection but also can reject continuous things like singing. - // NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor? + // NUMBER_OF_NOISE_SAMPLE_BLOCKS: How often should we re-evaluate the noise floor? float loudness = 0; int thisSample = 0; @@ -83,16 +83,16 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { const float NOISE_GATE_HEIGHT = 7.0f; const int NOISE_GATE_WIDTH = 5; - const int NOISE_GATE_CLOSE_FRAME_DELAY = 5; - const int NOISE_GATE_FRAMES_TO_AVERAGE = 5; + const int NOISE_GATE_CLOSE_BLOCK_DELAY = 5; + const int NOISE_GATE_BLOCKS_TO_AVERAGE = 5; // Check clipping, and check if should open noise gate - _didClipInLastFrame = false; + _didClipInLastBlock = false; for (int i = 0; i < numSamples; i++) { thisSample = std::abs(samples[i]); if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) { - _didClipInLastFrame = true; + _didClipInLastBlock = true; } loudness += thisSample; @@ -104,61 +104,64 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { _lastLoudness = fabs(loudness / numSamples); - if (_quietestFrame > _lastLoudness) { - _quietestFrame = _lastLoudness; + if (_quietestBlock > _lastLoudness) { + _quietestBlock = _lastLoudness; } - if (_loudestFrame < _lastLoudness) { - _loudestFrame = _lastLoudness; + if (_loudestBlock < _lastLoudness) { + _loudestBlock = _lastLoudness; } const int FRAMES_FOR_NOISE_DETECTION = 400; - if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) { - _quietestFrame = std::numeric_limits::max(); - _loudestFrame = 0.0f; - _inputFrameCounter = 0; + if (_inputBlockCounter++ > FRAMES_FOR_NOISE_DETECTION) { + _quietestBlock = std::numeric_limits::max(); + _loudestBlock = 0.0f; + _inputBlockCounter = 0; } // If Noise Gate is enabled, check and turn the gate on and off - float averageOfAllSampleFrames = 0.0f; - _sampleFrames[_sampleCounter++] = _lastLoudness; - if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) { + float averageOfAllSampleBlocks = 0.0f; + _sampleBlocks[_sampleCounter++] = _lastLoudness; + if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_BLOCKS) { float smallestSample = std::numeric_limits::max(); - for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i += NOISE_GATE_FRAMES_TO_AVERAGE) { + for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_BLOCKS - NOISE_GATE_BLOCKS_TO_AVERAGE; i += NOISE_GATE_BLOCKS_TO_AVERAGE) { float thisAverage = 0.0f; - for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) { - thisAverage += _sampleFrames[j]; - averageOfAllSampleFrames += _sampleFrames[j]; + for (int j = i; j < i + NOISE_GATE_BLOCKS_TO_AVERAGE; j++) { + thisAverage += _sampleBlocks[j]; + averageOfAllSampleBlocks += _sampleBlocks[j]; } - thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE; + thisAverage /= NOISE_GATE_BLOCKS_TO_AVERAGE; if (thisAverage < smallestSample) { smallestSample = thisAverage; } } - averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES; + averageOfAllSampleBlocks /= NUMBER_OF_NOISE_SAMPLE_BLOCKS; _measuredFloor = smallestSample; _sampleCounter = 0; } - _closedInLastFrame = false; - _openedInLastFrame = false; + _closedInLastBlock = false; + _openedInLastBlock = false; if (samplesOverNoiseGate > NOISE_GATE_WIDTH) { - _openedInLastFrame = !_isOpen; + _openedInLastBlock = !_isOpen; _isOpen = true; - _framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY; + _buffersToClose = NOISE_GATE_CLOSE_BLOCK_DELAY; } else { - if (--_framesToClose == 0) { - _closedInLastFrame = _isOpen; + if (--_buffersToClose == 0) { + _closedInLastBlock = _isOpen; _isOpen = false; } } if (!_isOpen) { - if (_closedInLastFrame) { - // would be nice to do a little crossfade to silence + // First buffer after being closed gets faded to silence, we fade across + // the entire buffer on fading out. All subsequent buffers are muted by being slammed + // to zeros + if (_closedInLastBlock) { + float fadeSlope = (1.0f / numSamples); for (int i = 0; i < numSamples; i++) { - float fadedSample = (1.0f - (float)i / (float)numSamples) * (float)samples[i]; + float fadedSample = (1.0f - ((float)i * fadeSlope)) * (float)samples[i]; samples[i] = (int16_t)fadedSample; } } else { @@ -167,10 +170,14 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { _lastLoudness = 0; } - if (_openedInLastFrame) { - // would be nice to do a little crossfade from silence - for (int i = 0; i < numSamples; i++) { - float fadedSample = ((float)i / (float)numSamples) * (float)samples[i]; + if (_openedInLastBlock) { + // would be nice to do a little crossfade from silence, but we only want to fade + // across the first 1/10th of the buffer, because we don't want to miss early + // transients. + int fadeSamples = numSamples / 10; // fade over 1/10th of the samples + float fadeSlope = (1.0f / fadeSamples); + for (int i = 0; i < fadeSamples; i++) { + float fadedSample = (float)i * fadeSlope * (float)samples[i]; samples[i] = (int16_t)fadedSample; } } diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio-client/src/AudioNoiseGate.h index fb31561994..81ef27664d 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio-client/src/AudioNoiseGate.h @@ -14,7 +14,7 @@ #include -const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300; +const int NUMBER_OF_NOISE_SAMPLE_BLOCKS = 300; class AudioNoiseGate { public: @@ -23,9 +23,9 @@ public: void gateSamples(int16_t* samples, int numSamples); void removeDCOffset(int16_t* samples, int numSamples); - bool clippedInLastFrame() const { return _didClipInLastFrame; } - bool closedInLastFrame() const { return _closedInLastFrame; } - bool openedInLastFrame() const { return _openedInLastFrame; } + bool clippedInLastBlock() const { return _didClipInLastBlock; } + bool closedInLastBlock() const { return _closedInLastBlock; } + bool openedInLastBlock() const { return _openedInLastBlock; } bool isOpen() const { return _isOpen; } float getMeasuredFloor() const { return _measuredFloor; } float getLastLoudness() const { return _lastLoudness; } @@ -33,19 +33,19 @@ public: static const float CLIPPING_THRESHOLD; private: - int _inputFrameCounter; + int _inputBlockCounter; float _lastLoudness; - float _quietestFrame; - float _loudestFrame; - bool _didClipInLastFrame; + float _quietestBlock; + float _loudestBlock; + bool _didClipInLastBlock; float _dcOffset; float _measuredFloor; - float _sampleFrames[NUMBER_OF_NOISE_SAMPLE_FRAMES]; + float _sampleBlocks[NUMBER_OF_NOISE_SAMPLE_BLOCKS]; int _sampleCounter; bool _isOpen; - bool _closedInLastFrame { false }; - bool _openedInLastFrame { false }; - int _framesToClose; + bool _closedInLastBlock { false }; + bool _openedInLastBlock { false }; + int _buffersToClose; }; #endif // hifi_AudioNoiseGate_h \ No newline at end of file From 099ee1db7f1ffda4248b3c3a4a4aabfd04b5a8be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 13 Mar 2017 12:05:42 -0700 Subject: [PATCH 22/32] CR feedback --- libraries/audio-client/src/AudioNoiseGate.cpp | 22 +++++++++---------- libraries/audio-client/src/AudioNoiseGate.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 2acdea6f0c..7aef8086a0 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -28,7 +28,7 @@ AudioNoiseGate::AudioNoiseGate() : _measuredFloor(0.0f), _sampleCounter(0), _isOpen(false), - _buffersToClose(0) + _blocksToClose(0) { } @@ -37,7 +37,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { // // DC Offset correction // - // Measure the DC offset over a trailing number of buffers, and remove it from the input signal. + // Measure the DC offset over a trailing number of blocks, and remove it from the input signal. // This causes the noise background measurements and server muting to be more accurate. Many off-board // ADC's have a noticeable DC offset. // @@ -51,7 +51,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) { // Update measured DC offset measuredDcOffset /= numSamples; if (_dcOffset == 0.0f) { - // On first buffer, copy over measured offset + // On first block, copy over measured offset _dcOffset = measuredDcOffset; } else { _dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset; @@ -69,11 +69,11 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { // // NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate. // Make this value lower for more sensitivity and less rejection of noise. - // NOISE_GATE_WIDTH: The number of samples in an audio buffer for which the height must be exceeded + // NOISE_GATE_WIDTH: The number of samples in an audio block for which the height must be exceeded // to open the gate. - // NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the buffer, how many buffers + // NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the block, how many blocks // will we wait before closing the gate. - // NOISE_GATE_BLOCKSS_TO_AVERAGE: How many audio buffers should we average together to compute noise floor. + // NOISE_GATE_BLOCKSS_TO_AVERAGE: How many audio blocks should we average together to compute noise floor. // More means better rejection but also can reject continuous things like singing. // NUMBER_OF_NOISE_SAMPLE_BLOCKS: How often should we re-evaluate the noise floor? @@ -147,16 +147,16 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { if (samplesOverNoiseGate > NOISE_GATE_WIDTH) { _openedInLastBlock = !_isOpen; _isOpen = true; - _buffersToClose = NOISE_GATE_CLOSE_BLOCK_DELAY; + _blocksToClose = NOISE_GATE_CLOSE_BLOCK_DELAY; } else { - if (--_buffersToClose == 0) { + if (--_blocksToClose == 0) { _closedInLastBlock = _isOpen; _isOpen = false; } } if (!_isOpen) { - // First buffer after being closed gets faded to silence, we fade across - // the entire buffer on fading out. All subsequent buffers are muted by being slammed + // First block after being closed gets faded to silence, we fade across + // the entire block on fading out. All subsequent blocks are muted by being slammed // to zeros if (_closedInLastBlock) { float fadeSlope = (1.0f / numSamples); @@ -172,7 +172,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { if (_openedInLastBlock) { // would be nice to do a little crossfade from silence, but we only want to fade - // across the first 1/10th of the buffer, because we don't want to miss early + // across the first 1/10th of the block, because we don't want to miss early // transients. int fadeSamples = numSamples / 10; // fade over 1/10th of the samples float fadeSlope = (1.0f / fadeSamples); diff --git a/libraries/audio-client/src/AudioNoiseGate.h b/libraries/audio-client/src/AudioNoiseGate.h index 81ef27664d..f72e92b0d5 100644 --- a/libraries/audio-client/src/AudioNoiseGate.h +++ b/libraries/audio-client/src/AudioNoiseGate.h @@ -45,7 +45,7 @@ private: bool _isOpen; bool _closedInLastBlock { false }; bool _openedInLastBlock { false }; - int _buffersToClose; + int _blocksToClose; }; #endif // hifi_AudioNoiseGate_h \ No newline at end of file From 956fdd09339378ae25ecc17a3fbb262d3f0c59ad Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 13 Mar 2017 12:06:38 -0700 Subject: [PATCH 23/32] CR feedback --- libraries/audio-client/src/AudioNoiseGate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioNoiseGate.cpp b/libraries/audio-client/src/AudioNoiseGate.cpp index 7aef8086a0..8a9134b5dc 100644 --- a/libraries/audio-client/src/AudioNoiseGate.cpp +++ b/libraries/audio-client/src/AudioNoiseGate.cpp @@ -73,7 +73,7 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) { // to open the gate. // NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the block, how many blocks // will we wait before closing the gate. - // NOISE_GATE_BLOCKSS_TO_AVERAGE: How many audio blocks should we average together to compute noise floor. + // NOISE_GATE_BLOCKS_TO_AVERAGE: How many audio blocks should we average together to compute noise floor. // More means better rejection but also can reject continuous things like singing. // NUMBER_OF_NOISE_SAMPLE_BLOCKS: How often should we re-evaluate the noise floor? From d9ca8a826d4ea438db10693f07d7a580e0035b3c Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Tue, 14 Mar 2017 17:02:09 +0100 Subject: [PATCH 24/32] TabletEntityStatistics.qml refactored. Text now wrapped. TODO: coloring, server stats --- .../hifi/dialogs/TabletEntityStatistics.qml | 144 +++--------------- .../dialogs/TabletEntityStatisticsItem.qml | 48 ++++++ interface/src/ui/OctreeStatsProvider.cpp | 1 + 3 files changed, 74 insertions(+), 119 deletions(-) create mode 100644 interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index bd7796927e..f6fdf90474 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -55,179 +55,91 @@ Rectangle { y: hifi.dimensions.tabletMenuHeader //-bgNavBar spacing: 20 - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Elements on Servers:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: elementsOnServerLabel - size: 16 + titleText: qsTr("Elements on Servers:") text: OctreeStats.serverElements - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Local Elements:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: localElementsLabel - size: 16 + titleText: qsTr("Local Elements:") text: OctreeStats.localElements - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Elements Memory:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: elementsMemoryLabel - size: 16 + titleText: qsTr("Elements Memory:") text: OctreeStats.localElementsMemory - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Sending Mode:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: sendingModeLabel - size: 16 + titleText: qsTr("Sending Mode:") text: OctreeStats.sendingMode - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Incoming Entity Packets:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: incomingEntityPacketsLabel - size: 16 + titleText: qsTr("Incoming Entity Packets:") text: OctreeStats.processedPackets - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Processed Packets Elements:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: processedPacketsElementsLabel - size: 16 + titleText: qsTr("Processed Packets Elements:") text: OctreeStats.processedPacketsElements - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Processed Packets Entities:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: processedPacketsEntitiesLabel - size: 16 + titleText: qsTr("Processed Packets Entities:") text: OctreeStats.processedPacketsEntities - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Processed Packets Timing:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: processedPacketsTimingLabel - size: 16 + titleText: qsTr("Processed Packets Timing:") text: OctreeStats.processedPacketsTiming - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Outbound Entity Packets:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: outboundEntityPacketsLabel - size: 16 + titleText: qsTr("Outbound Entity Packets:") text: OctreeStats.outboundEditPackets - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Entity Update Time:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: entityUpdateTimeLabel - size: 16 + titleText: qsTr("Entity Update Time:") text: OctreeStats.entityUpdateTime - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } - HifiControls.Label { - size: 20 + TabletEntityStatisticsItem { anchors.left: parent.left anchors.right: parent.right - text: qsTr("Entity Updates:") - colorScheme: root.colorScheme - } - HifiControls.Label { - id: entityUpdatesLabel - size: 16 + titleText: qsTr("Entity Updates:") text: OctreeStats.entityUpdates - anchors.left: parent.left - anchors.right: parent.right colorScheme: root.colorScheme } @@ -242,18 +154,12 @@ Rectangle { state: "less" - HifiControls.Label { - size: 20 - width: parent.width - text: qsTr("Entity Server ") + (index+1) + ":" - colorScheme: root.colorScheme - } - HifiControls.Label { - id: entityServer1Label - size: 20 + TabletEntityStatisticsItem { width: parent.width + titleText: qsTr("Entity Server ") + (index+1) + ":" colorScheme: root.colorScheme } + Row { id: buttonsRow width: parent.width diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml new file mode 100644 index 0000000000..c4509d5a76 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml @@ -0,0 +1,48 @@ +// +// TabletEntityStatistics.qml +// +// Created by Vlad Stelmahovsky on 3/11/17 +// Copyright 2017 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import Qt.labs.settings 1.0 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls + +Column { + id: root + property int colorScheme: hifi.colorSchemes.dark + + property alias titleText: titleLabel.text + property alias text: valueLabel.text + + HifiConstants { id: hifi } + + anchors.left: parent.left + anchors.right: parent.right + spacing: 10 + + HifiControls.Label { + id: titleLabel + size: 20 + anchors.left: parent.left + anchors.right: parent.right + colorScheme: root.colorScheme + } + + RalewaySemiBold { + id: valueLabel + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + size: 16 + color: enabled ? (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGray : hifi.colors.lightGrayText) + : (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight); + } +} diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index 057056d8ce..e8d863e2b8 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -151,6 +151,7 @@ void OctreeStatsProvider::updateOctreeStatsData() { unsigned long totalInternal = 0; unsigned long totalLeaves = 0; + m_sendingMode.clear(); NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); sceneStats->withReadLock([&] { for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { From 56d4bfe07742b44bde6cb6e5998b1c3025c476fe Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Tue, 14 Mar 2017 18:38:49 +0100 Subject: [PATCH 25/32] Servers stats added. TODO: coloring --- .../hifi/dialogs/TabletEntityStatistics.qml | 12 +- interface/src/ui/OctreeStatsProvider.cpp | 261 ++++++------------ interface/src/ui/OctreeStatsProvider.h | 11 +- 3 files changed, 112 insertions(+), 172 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index f6fdf90474..9d3829e743 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -46,6 +46,7 @@ Rectangle { anchors.bottomMargin: hifi.dimensions.tabletMenuHeader contentWidth: column.implicitWidth contentHeight: column.implicitHeight + boundsBehavior: Flickable.StopAtBounds Column { id: column @@ -155,6 +156,7 @@ Rectangle { state: "less" TabletEntityStatisticsItem { + id: serverStats width: parent.width titleText: qsTr("Entity Server ") + (index+1) + ":" colorScheme: root.colorScheme @@ -178,7 +180,7 @@ Rectangle { } else if (serverColumn.state === "more") { serverColumn.state = "most" } else { - serverColumn.state = "less" + serverColumn.state = "more" } } } @@ -194,7 +196,7 @@ Rectangle { } else if (serverColumn.state === "more") { serverColumn.state = "less" } else { - serverColumn.state = "more" + serverColumn.state = "less" } } @@ -205,16 +207,22 @@ Rectangle { name: "less" PropertyChanges { target: moreButton; text: qsTr("more..."); } PropertyChanges { target: mostButton; text: qsTr("most..."); } + PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3]; } }, State { name: "more" PropertyChanges { target: moreButton; text: qsTr("most..."); } PropertyChanges { target: mostButton; text: qsTr("less..."); } + PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] + + OctreeStats.servers[index*3 + 1]; } }, State { name: "most" PropertyChanges { target: moreButton; text: qsTr("less..."); } PropertyChanges { target: mostButton; text: qsTr("least..."); } + PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] + + OctreeStats.servers[index*3 + 1] + + OctreeStats.servers[index*3 + 2]; } } ] } //servers column diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index e8d863e2b8..e51172ff55 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -9,11 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include - #include "Application.h" #include "../octree/OctreePacketProcessor.h" @@ -32,23 +27,6 @@ OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats _updateTimer.setSingleShot(true); } -void OctreeStatsProvider::moreless(const QString& link) { - QStringList linkDetails = link.split("-"); - const int COMMAND_ITEM = 0; - const int SERVER_NUMBER_ITEM = 1; - QString serverNumberString = linkDetails[SERVER_NUMBER_ITEM]; - QString command = linkDetails[COMMAND_ITEM]; - int serverNumber = serverNumberString.toInt(); - -// if (command == "more") { -// _extraServerDetails[serverNumber-1] = MORE; -// } else if (command == "most") { -// _extraServerDetails[serverNumber-1] = MOST; -// } else { -// _extraServerDetails[serverNumber-1] = LESS; -// } -} - /* * Start updates statistics */ @@ -66,9 +44,9 @@ void OctreeStatsProvider::stopUpdates() { //int OctreeStatsProvider::AddStatItem(const char* caption, unsigned colorRGBA) { // const int STATS_LABEL_WIDTH = 600; - + // _statCount++; // increment our current stat count - + // if (colorRGBA == 0) { // static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; // colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; @@ -77,7 +55,7 @@ void OctreeStatsProvider::stopUpdates() { // // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) // //QPalette palette = label->palette(); - + // // This goofiness came from the bandwidth meter code, it basically stores a color in an unsigned and extracts it // unsigned rgb = colorRGBA >> 8; // const unsigned colorpart1 = 0xfefefeu; @@ -87,7 +65,7 @@ void OctreeStatsProvider::stopUpdates() { // label->setPalette(palette); // _form->addRow(QString(" %1:").arg(caption), label); // label->setFixedWidth(STATS_LABEL_WIDTH); - + // return _statCount; //} @@ -278,8 +256,7 @@ void OctreeStatsProvider::updateOctreeServers() { int serverCount = 0; showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity", - qApp->getEntityServerJurisdictions()); - //qDebug() << "vladest: octree servers:" << serverCount; + qApp->getEntityServerJurisdictions()); if (m_serversNum != serverCount) { m_serversNum = serverCount; emit serversNumChanged(m_serversNum); @@ -287,34 +264,25 @@ void OctreeStatsProvider::updateOctreeServers() { } void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName, - NodeToJurisdictionMap& serverJurisdictions) { - - QLocale locale(QLocale::English); - + NodeToJurisdictionMap& serverJurisdictions) { + + m_servers.clear(); + auto nodeList = DependencyManager::get(); - nodeList->eachNode([&](const SharedNodePointer& node){ + nodeList->eachNode([&](const SharedNodePointer& node) { // only send to the NodeTypes that are NodeType_t_VOXEL_SERVER if (node->getType() == serverType) { serverCount++; -// if (serverCount > _octreeServerLabelsCount) { -// QString label = QString("%1 Server %2").arg(serverTypeName).arg(serverCount); -// //int thisServerRow = _octreeServerLables[serverCount-1] = AddStatItem(label.toUtf8().constData()); -//// _labels[thisServerRow]->setTextFormat(Qt::RichText); -//// _labels[thisServerRow]->setTextInteractionFlags(Qt::TextBrowserInteraction); -//// connect(_labels[thisServerRow], SIGNAL(linkActivated(const QString&)), this, SLOT(moreless(const QString&))); -// _octreeServerLabelsCount++; -// } - - std::stringstream serverDetails(""); - std::stringstream extraDetails(""); - std::stringstream linkDetails(""); + QString lesserDetails; + QString moreDetails; + QString mostDetails; if (node->getActiveSocket()) { - serverDetails << "active "; + lesserDetails += "active "; } else { - serverDetails << "inactive "; + lesserDetails += "inactive "; } QUuid nodeUUID = node->getUUID(); @@ -323,9 +291,9 @@ void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t s // missing at least one jurisdiction serverJurisdictions.withReadLock([&] { if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) { - serverDetails << " unknown jurisdiction "; + lesserDetails += " unknown jurisdiction "; return; - } + } const JurisdictionMap& map = serverJurisdictions[nodeUUID]; auto rootCode = map.getRootOctalCode(); @@ -336,135 +304,90 @@ void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t s VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode.get(), rootDetails); AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); - serverDetails << " jurisdiction: " - << qPrintable(rootCodeHex) - << " [" - << rootDetails.x << ", " - << rootDetails.y << ", " - << rootDetails.z << ": " - << rootDetails.s << "] "; + lesserDetails += QString(" jurisdiction: %1 [%2, %3, %4: %5]") + .arg(rootCodeHex) + .arg(rootDetails.x) + .arg(rootDetails.y) + .arg(rootDetails.z) + .arg(rootDetails.s); } else { - serverDetails << " jurisdiction has no rootCode"; + lesserDetails += " jurisdiction has no rootCode"; } // root code }); // now lookup stats details for this server... - if (/*_extraServerDetails[serverCount-1] != LESS*/true) { - NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); - sceneStats->withReadLock([&] { - if (sceneStats->find(nodeUUID) != sceneStats->end()) { - OctreeSceneStats& stats = sceneStats->at(nodeUUID); -/* - switch (_extraServerDetails[serverCount - 1]) { - case MOST: { - extraDetails << "
"; + NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); + sceneStats->withReadLock([&] { + if (sceneStats->find(nodeUUID) != sceneStats->end()) { + OctreeSceneStats& stats = sceneStats->at(nodeUUID); - float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; - float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; - float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; - float lastFullPackets = stats.getLastFullTotalPackets(); - float lastFullPPS = lastFullPackets; - if (lastFullSendInSeconds > 0) { - lastFullPPS = lastFullPackets / lastFullSendInSeconds; - } - - QString lastFullEncodeString = locale.toString(lastFullEncode); - QString lastFullSendString = locale.toString(lastFullSend); - QString lastFullPacketsString = locale.toString(lastFullPackets); - QString lastFullBytesString = locale.toString((uint)stats.getLastFullTotalBytes()); - QString lastFullPPSString = locale.toString(lastFullPPS); - - extraDetails << "
" << "Last Full Scene... " << - "Encode: " << qPrintable(lastFullEncodeString) << " ms " << - "Send: " << qPrintable(lastFullSendString) << " ms " << - "Packets: " << qPrintable(lastFullPacketsString) << " " << - "Bytes: " << qPrintable(lastFullBytesString) << " " << - "Rate: " << qPrintable(lastFullPPSString) << " PPS"; - - for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { - OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); - OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); - extraDetails << "
" << itemInfo.caption << " " << stats.getItemValue(item); - } - } // fall through... since MOST has all of MORE - case MORE: { - QString totalString = locale.toString((uint)stats.getTotalElements()); - QString internalString = locale.toString((uint)stats.getTotalInternal()); - QString leavesString = locale.toString((uint)stats.getTotalLeaves()); - - serverDetails << "
" << "Node UUID: " << qPrintable(nodeUUID.toString()) << " "; - - serverDetails << "
" << "Elements: " << - qPrintable(totalString) << " total " << - qPrintable(internalString) << " internal " << - qPrintable(leavesString) << " leaves "; - - QString incomingPacketsString = locale.toString((uint)stats.getIncomingPackets()); - QString incomingBytesString = locale.toString((uint)stats.getIncomingBytes()); - QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes()); - const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); - QString incomingOutOfOrderString = locale.toString((uint)seqStats.getOutOfOrder()); - QString incomingLateString = locale.toString((uint)seqStats.getLate()); - QString incomingUnreasonableString = locale.toString((uint)seqStats.getUnreasonable()); - QString incomingEarlyString = locale.toString((uint)seqStats.getEarly()); - QString incomingLikelyLostString = locale.toString((uint)seqStats.getLost()); - QString incomingRecovered = locale.toString((uint)seqStats.getRecovered()); - - qint64 clockSkewInUsecs = node->getClockSkewUsec(); - QString formattedClockSkewString = formatUsecTime(clockSkewInUsecs); - qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; - QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage()); - QString incomingPingTimeString = locale.toString(node->getPingMs()); - QString incomingClockSkewString = locale.toString(clockSkewInMS); - - serverDetails << "
" << "Incoming Packets: " << qPrintable(incomingPacketsString) << - "/ Lost: " << qPrintable(incomingLikelyLostString) << - "/ Recovered: " << qPrintable(incomingRecovered); - - serverDetails << "
" << " Out of Order: " << qPrintable(incomingOutOfOrderString) << - "/ Early: " << qPrintable(incomingEarlyString) << - "/ Late: " << qPrintable(incomingLateString) << - "/ Unreasonable: " << qPrintable(incomingUnreasonableString); - - serverDetails << "
" << - " Average Flight Time: " << qPrintable(incomingFlightTimeString) << " msecs"; - - serverDetails << "
" << - " Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs"; - - serverDetails << "
" << - " Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" << - " [" << qPrintable(formattedClockSkewString) << "]"; - - - serverDetails << "
" << "Incoming" << - " Bytes: " << qPrintable(incomingBytesString) << - " Wasted Bytes: " << qPrintable(incomingWastedBytesString); - - serverDetails << extraDetails.str(); - if (_extraServerDetails[serverCount - 1] == MORE) { - linkDetails << " " << " [most...]"; - linkDetails << " " << " [less...]"; - } else { - linkDetails << " " << " [less...]"; - linkDetails << " " << " [least...]"; - } - - } break; - case LESS: { - // nothing - } break; - }*/ + float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC; + float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC; + float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND; + float lastFullPackets = stats.getLastFullTotalPackets(); + float lastFullPPS = lastFullPackets; + if (lastFullSendInSeconds > 0) { + lastFullPPS = lastFullPackets / lastFullSendInSeconds; } - }); - } else { - linkDetails << " " << " [more...]"; - linkDetails << " " << " [most...]"; - } - serverDetails << linkDetails.str(); - //_labels[_octreeServerLables[serverCount - 1]]->setText(serverDetails.str().c_str()); + + mostDetails += QString("

Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS") + .arg(lastFullEncode) + .arg(lastFullSend) + .arg(lastFullPackets) + .arg(stats.getLastFullTotalBytes()) + .arg(lastFullPPS); + + for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) { + OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i); + OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item); + mostDetails += QString("
%1 %2") + .arg(itemInfo.caption).arg(stats.getItemValue(item)); + } + + moreDetails += "
Node UUID: " +nodeUUID.toString() + " "; + + moreDetails += QString("
Elements: %1 total %2 internal %3 leaves ") + .arg(stats.getTotalElements()) + .arg(stats.getTotalInternal()) + .arg(stats.getTotalLeaves()); + + const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats(); + qint64 clockSkewInUsecs = node->getClockSkewUsec(); + qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC; + + moreDetails += QString("
Incoming Packets: %1/ Lost: %2/ Recovered: %3") + .arg(stats.getIncomingPackets()) + .arg(seqStats.getLost()) + .arg(seqStats.getRecovered()); + + moreDetails += QString("
Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4") + .arg(seqStats.getOutOfOrder()) + .arg(seqStats.getEarly()) + .arg(seqStats.getLate()) + .arg(seqStats.getUnreasonable()); + + moreDetails += QString("
Average Flight Time: %1 msecs") + .arg(stats.getIncomingFlightTimeAverage()); + + moreDetails += QString("
Average Ping Time: %1 msecs") + .arg(node->getPingMs()); + + moreDetails += QString("
Average Clock Skew: %1 msecs [%2]") + .arg(clockSkewInMS) + .arg(formatUsecTime(clockSkewInUsecs)); + + + moreDetails += QString("
Incoming Bytes: %1 Wasted Bytes: %2") + .arg(stats.getIncomingBytes()) + .arg(stats.getIncomingWastedBytes()); + } + }); + m_servers.append(lesserDetails); + m_servers.append(moreDetails); + m_servers.append(mostDetails); } // is VOXEL_SERVER }); + emit serversChanged(m_servers); } diff --git a/interface/src/ui/OctreeStatsProvider.h b/interface/src/ui/OctreeStatsProvider.h index 6fc3e091f4..6c4f27149d 100644 --- a/interface/src/ui/OctreeStatsProvider.h +++ b/interface/src/ui/OctreeStatsProvider.h @@ -14,6 +14,7 @@ #include #include +#include #include "DependencyManager.h" @@ -38,6 +39,8 @@ class OctreeStatsProvider : public QObject, public Dependency { Q_PROPERTY(QString entityUpdateTime READ entityUpdateTime NOTIFY entityUpdateTimeChanged) Q_PROPERTY(QString entityUpdates READ entityUpdates NOTIFY entityUpdatesChanged) + Q_PROPERTY(QStringList servers READ servers NOTIFY serversChanged) + public: OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model); ~OctreeStatsProvider(); @@ -88,6 +91,10 @@ public: return m_entityUpdates; } + QStringList servers() const { + return m_servers; + } + signals: void serversNumChanged(int serversNum); @@ -103,8 +110,9 @@ signals: void entityUpdateTimeChanged(const QString &entityUpdateTime); void entityUpdatesChanged(const QString &entityUpdates); + void serversChanged(const QStringList &servers); + public slots: - void moreless(const QString& link); void startUpdates(); void stopUpdates(); @@ -139,6 +147,7 @@ private: QString m_outboundEditPackets; QString m_entityUpdateTime; QString m_entityUpdates; + QStringList m_servers; }; #endif // hifi_OctreeStatsProvider_h From e2a4011b05edff03847e80fe4930466db6e31958 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Tue, 14 Mar 2017 19:10:41 +0100 Subject: [PATCH 26/32] Coloring implemented --- .../hifi/dialogs/TabletEntityStatistics.qml | 12 ++++++ .../dialogs/TabletEntityStatisticsItem.qml | 1 + interface/src/ui/OctreeStatsProvider.cpp | 40 ++++++------------- interface/src/ui/OctreeStatsProvider.h | 3 +- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index 9d3829e743..35ee58be0c 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -62,6 +62,7 @@ Rectangle { titleText: qsTr("Elements on Servers:") text: OctreeStats.serverElements colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -70,6 +71,7 @@ Rectangle { titleText: qsTr("Local Elements:") text: OctreeStats.localElements colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -78,6 +80,7 @@ Rectangle { titleText: qsTr("Elements Memory:") text: OctreeStats.localElementsMemory colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -86,6 +89,7 @@ Rectangle { titleText: qsTr("Sending Mode:") text: OctreeStats.sendingMode colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -94,6 +98,7 @@ Rectangle { titleText: qsTr("Incoming Entity Packets:") text: OctreeStats.processedPackets colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -102,6 +107,7 @@ Rectangle { titleText: qsTr("Processed Packets Elements:") text: OctreeStats.processedPacketsElements colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -110,6 +116,7 @@ Rectangle { titleText: qsTr("Processed Packets Entities:") text: OctreeStats.processedPacketsEntities colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -118,6 +125,7 @@ Rectangle { titleText: qsTr("Processed Packets Timing:") text: OctreeStats.processedPacketsTiming colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -126,6 +134,7 @@ Rectangle { titleText: qsTr("Outbound Entity Packets:") text: OctreeStats.outboundEditPackets colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -134,6 +143,7 @@ Rectangle { titleText: qsTr("Entity Update Time:") text: OctreeStats.entityUpdateTime colorScheme: root.colorScheme + color: OctreeStats.getColor() } TabletEntityStatisticsItem { @@ -142,6 +152,7 @@ Rectangle { titleText: qsTr("Entity Updates:") text: OctreeStats.entityUpdates colorScheme: root.colorScheme + color: OctreeStats.getColor() } Repeater { @@ -160,6 +171,7 @@ Rectangle { width: parent.width titleText: qsTr("Entity Server ") + (index+1) + ":" colorScheme: root.colorScheme + color: OctreeStats.getColor() } Row { diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml index c4509d5a76..894a4c1813 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml @@ -21,6 +21,7 @@ Column { property alias titleText: titleLabel.text property alias text: valueLabel.text + property alias color: valueLabel.color HifiConstants { id: hifi } diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index e51172ff55..95092d4253 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -41,33 +41,17 @@ void OctreeStatsProvider::stopUpdates() { _updateTimer.stop(); } - -//int OctreeStatsProvider::AddStatItem(const char* caption, unsigned colorRGBA) { -// const int STATS_LABEL_WIDTH = 600; - -// _statCount++; // increment our current stat count - -// if (colorRGBA == 0) { -// static unsigned rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; -// colorRGBA = rotatingColors[_statCount % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; -// } -// //QLabel* label = _labels[_statCount] = new QLabel(); - -// // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) -// //QPalette palette = label->palette(); - -// // This goofiness came from the bandwidth meter code, it basically stores a color in an unsigned and extracts it -// unsigned rgb = colorRGBA >> 8; -// const unsigned colorpart1 = 0xfefefeu; -// const unsigned colorpart2 = 0xf8f8f8; -// rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3); -// palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb)); -// label->setPalette(palette); -// _form->addRow(QString(" %1:").arg(caption), label); -// label->setFixedWidth(STATS_LABEL_WIDTH); - -// return _statCount; -//} +QColor OctreeStatsProvider::getColor() const { + static int statIndex = 1; + static quint32 rotatingColors[] = { GREENISH, YELLOWISH, GREYISH }; + quint32 colorRGBA = rotatingColors[statIndex % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))]; + quint32 rgb = colorRGBA >> 8; + const quint32 colorpart1 = 0xfefefeu; + const quint32 colorpart2 = 0xf8f8f8; + rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3); + statIndex++; + return QColor::fromRgb(rgb); +} OctreeStatsProvider::~OctreeStatsProvider() { _updateTimer.stop(); @@ -108,7 +92,7 @@ void OctreeStatsProvider::updateOctreeStatsData() { // Only refresh our stats every once in a while, unless asked for realtime //if no realtime, then update once per second. Otherwise consider 60FPS update, ie 16ms interval //int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000; - _updateTimer.start(SAMPLING_WINDOW/1000); + _updateTimer.start(REFRESH_AFTER/1000); const int FLOATING_POINT_PRECISION = 3; diff --git a/interface/src/ui/OctreeStatsProvider.h b/interface/src/ui/OctreeStatsProvider.h index 6c4f27149d..c919ca102f 100644 --- a/interface/src/ui/OctreeStatsProvider.h +++ b/interface/src/ui/OctreeStatsProvider.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include "DependencyManager.h" @@ -115,6 +115,7 @@ signals: public slots: void startUpdates(); void stopUpdates(); + QColor getColor() const; private slots: void updateOctreeStatsData(); From 3a788d31ded783d12a55f810c626511bb640fee8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 14 Mar 2017 15:34:41 -0700 Subject: [PATCH 27/32] Tablet Running Scripts dialog fixes * The "Currrently Running" section is now visible by default, this includes the "Reload All" and "Remove All" buttons * The entire dialog is flickable/scrollable. We need to do this because the entire dialog is too tall to fit on the tablet. * The keyboard now appears when the filter edit text has focus. The content is scrolled in such away that the focus edit field, and the tree view are above the keyboard. --- .../qml/hifi/dialogs/TabletRunningScripts.qml | 505 ++++++++++-------- .../qml/hifi/tablet/TabletMenuStack.qml | 6 +- 2 files changed, 273 insertions(+), 238 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index b4a1ab01b6..dee0d0e21f 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -79,266 +79,297 @@ Rectangle { scripts.stopAllScripts(); } - Column { + Flickable { + id: flickable width: parent.width - HifiControls.TabletContentSection { - name: "Currently Running" - isFirst: true + height: parent.height - (keyboard.raised ? keyboard.raisedHeight : 0) + contentWidth: parent.width + contentHeight: column.childrenRect.height + clip: true - HifiControls.VerticalSpacer {} + Column { + id: column + width: parent.width + HifiControls.TabletContentSection { + id: firstSection + name: "Currently Running" + isFirst: true - Row { - spacing: hifi.dimensions.contentSpacing.x + HifiControls.VerticalSpacer {} - HifiControls.Button { - text: "Reload All" - color: hifi.buttons.black - onClicked: reloadAll() - } + Row { + spacing: hifi.dimensions.contentSpacing.x - HifiControls.Button { - text: "Remove All" - color: hifi.buttons.red - onClicked: stopAll() - } - } - - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border - } - - HifiControls.Table { - model: runningScriptsModel - id: table - height: 185 - colorScheme: hifi.colorSchemes.dark - anchors.left: parent.left - anchors.right: parent.right - expandSelectedRow: true - - itemDelegate: Item { - anchors { - left: parent ? parent.left : undefined - leftMargin: hifi.dimensions.tablePadding - right: parent ? parent.right : undefined - rightMargin: hifi.dimensions.tablePadding + HifiControls.Button { + text: "Reload All" + color: hifi.buttons.black + onClicked: reloadAll() } - FiraSansSemiBold { - id: textItem - text: styleData.value - size: hifi.fontSizes.tableText - color: table.colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + HifiControls.Button { + text: "Remove All" + color: hifi.buttons.red + onClicked: stopAll() + } + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border + } + + HifiControls.Table { + model: runningScriptsModel + id: table + height: 185 + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + anchors.right: parent.right + expandSelectedRow: true + + itemDelegate: Item { anchors { - left: parent.left - right: parent.right - top: parent.top - topMargin: 3 + left: parent ? parent.left : undefined + leftMargin: hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding } - HiFiGlyphs { - id: reloadButton - text: hifi.glyphs.reloadSmall - color: reloadButtonArea.pressed ? hifi.colors.white : parent.color + FiraSansSemiBold { + id: textItem + text: styleData.value + size: hifi.fontSizes.tableText + color: table.colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) anchors { - top: parent.top - right: stopButton.left - verticalCenter: parent.verticalCenter - } - MouseArea { - id: reloadButtonArea - anchors { fill: parent; margins: -2 } - onClicked: reloadScript(model.url) - } - } - - HiFiGlyphs { - id: stopButton - text: hifi.glyphs.closeSmall - color: stopButtonArea.pressed ? hifi.colors.white : parent.color - anchors { - top: parent.top + left: parent.left right: parent.right - verticalCenter: parent.verticalCenter + top: parent.top + topMargin: 3 } - MouseArea { - id: stopButtonArea - anchors { fill: parent; margins: -2 } - onClicked: stopScript(model.url) + + HiFiGlyphs { + id: reloadButton + text: hifi.glyphs.reloadSmall + color: reloadButtonArea.pressed ? hifi.colors.white : parent.color + anchors { + top: parent.top + right: stopButton.left + verticalCenter: parent.verticalCenter + } + MouseArea { + id: reloadButtonArea + anchors { fill: parent; margins: -2 } + onClicked: reloadScript(model.url) + } + } + + HiFiGlyphs { + id: stopButton + text: hifi.glyphs.closeSmall + color: stopButtonArea.pressed ? hifi.colors.white : parent.color + anchors { + top: parent.top + right: parent.right + verticalCenter: parent.verticalCenter + } + MouseArea { + id: stopButtonArea + anchors { fill: parent; margins: -2 } + onClicked: stopScript(model.url) + } + } + + } + + FiraSansSemiBold { + text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : "" + elide: Text.ElideMiddle + size: hifi.fontSizes.tableText + color: table.colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + anchors { + top: textItem.bottom + left: parent.left + right: parent.right + } + visible: styleData.selected + } + } + + TableViewColumn { + role: "name" + } + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border + } + } + + HifiControls.TabletContentSection { + name: "Load Scripts" + + HifiControls.VerticalSpacer {} + + Row { + spacing: hifi.dimensions.contentSpacing.x + + HifiControls.Button { + text: "from URL" + color: hifi.buttons.black + height: 26 + onClicked: fromUrlTimer.running = true + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + Timer { + id: fromUrlTimer + interval: 5 + repeat: false + running: false + onTriggered: ApplicationInterface.loadScriptURLDialog(); + } + } + + HifiControls.Button { + text: "from Disk" + color: hifi.buttons.black + height: 26 + onClicked: fromDiskTimer.running = true + + Timer { + id: fromDiskTimer + interval: 5 + repeat: false + running: false + onTriggered: ApplicationInterface.loadDialog(); + } + } + + HifiControls.Button { + text: "Load Defaults" + color: hifi.buttons.black + height: 26 + onClicked: loadDefaults() + } + } + + HifiControls.VerticalSpacer {} + + HifiControls.TextField { + id: filterEdit + isSearchField: true + anchors.left: parent.left + anchors.right: parent.right + colorScheme: hifi.colorSchemes.dark + placeholderText: "Filter" + onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i") + Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i") + onActiveFocusChanged: { + // raise the keyboard + keyboard.raised = activeFocus; + + // scroll to the bottom of the content area. + if (activeFocus) { + flickable.contentY = (flickable.contentHeight - flickable.height); + } + } + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border + } + + HifiControls.Tree { + id: treeView + height: 155 + treeModel: scriptsModel + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + anchors.right: parent.right + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border + } + + HifiControls.TextField { + id: selectedScript + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x + + colorScheme: hifi.colorSchemes.dark + readOnly: true + + Connections { + target: treeView + onCurrentIndexChanged: { + var path = scriptsModel.data(treeView.currentIndex, 0x100) + if (path) { + selectedScript.text = path + } else { + selectedScript.text = "" } } - - } - - FiraSansSemiBold { - text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : "" - elide: Text.ElideMiddle - size: hifi.fontSizes.tableText - color: table.colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - anchors { - top: textItem.bottom - left: parent.left - right: parent.right - } - visible: styleData.selected } } - TableViewColumn { - role: "name" - } - } - - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border - } - } - - HifiControls.TabletContentSection { - name: "Load Scripts" - - HifiControls.VerticalSpacer {} - - Row { - spacing: hifi.dimensions.contentSpacing.x - - HifiControls.Button { - text: "from URL" - color: hifi.buttons.black - height: 26 - onClicked: fromUrlTimer.running = true - - // For some reason trigginer an API that enters - // an internal event loop directly from the button clicked - // trigger below causes the appliction to behave oddly. - // Most likely because the button onClicked handling is never - // completed until the function returns. - // FIXME find a better way of handling the input dialogs that - // doesn't trigger this. - Timer { - id: fromUrlTimer - interval: 5 - repeat: false - running: false - onTriggered: ApplicationInterface.loadScriptURLDialog(); - } - } - - HifiControls.Button { - text: "from Disk" - color: hifi.buttons.black - height: 26 - onClicked: fromDiskTimer.running = true - - Timer { - id: fromDiskTimer - interval: 5 - repeat: false - running: false - onTriggered: ApplicationInterface.loadDialog(); - } - } - - HifiControls.Button { - text: "Load Defaults" - color: hifi.buttons.black - height: 26 - onClicked: loadDefaults() - } - } - - HifiControls.VerticalSpacer {} - - HifiControls.TextField { - id: filterEdit - isSearchField: true - anchors.left: parent.left - anchors.right: parent.right - colorScheme: hifi.colorSchemes.dark - placeholderText: "Filter" - onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i") - Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i") - } - - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border - } - - HifiControls.Tree { - id: treeView - height: 155 - treeModel: scriptsModel - colorScheme: hifi.colorSchemes.dark - anchors.left: parent.left - anchors.right: parent.right - } - - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border - } - - HifiControls.TextField { - id: selectedScript - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x - - colorScheme: hifi.colorSchemes.dark - readOnly: true - - Connections { - target: treeView - onCurrentIndexChanged: { - var path = scriptsModel.data(treeView.currentIndex, 0x100) - if (path) { - selectedScript.text = path - } else { - selectedScript.text = "" - } - } - } - } - - Item { - // Take the loadButton out of the column flow. - id: loadButtonContainer - anchors.top: selectedScript.top - anchors.right: parent.right - - HifiControls.Button { - id: loadButton + Item { + // Take the loadButton out of the column flow. + id: loadButtonContainer + anchors.top: selectedScript.top anchors.right: parent.right - text: "Load" - color: hifi.buttons.blue - enabled: selectedScript.text != "" - onClicked: root.loadScript(selectedScript.text) + HifiControls.Button { + id: loadButton + anchors.right: parent.right + + text: "Load" + color: hifi.buttons.blue + enabled: selectedScript.text != "" + onClicked: root.loadScript(selectedScript.text) + } + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0) + } + + HifiControls.TextAction { + id: directoryButton + icon: hifi.glyphs.script + iconSize: 24 + text: "Reveal Scripts Folder" + onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath) + colorScheme: hifi.colorSchemes.dark + anchors.left: parent.left + visible: !isHMD + } + + HifiControls.VerticalSpacer { + height: hifi.dimensions.controlInterlineHeight - 3 + visible: !isHMD } } + } + } - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0) - } - - HifiControls.TextAction { - id: directoryButton - icon: hifi.glyphs.script - iconSize: 24 - text: "Reveal Scripts Folder" - onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath) - colorScheme: hifi.colorSchemes.dark - anchors.left: parent.left - visible: !isHMD - } - - HifiControls.VerticalSpacer { - height: hifi.dimensions.controlInterlineHeight - 3 - visible: !isHMD - } + HifiControls.Keyboard { + id: keyboard + raised: false + numeric: false + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right } } } diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index f77ad0d4b8..bacc11228e 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -17,7 +17,7 @@ Item { id: root anchors.fill: parent objectName: "tabletMenuHandlerItem" - + StackView { anchors.fill: parent id: d @@ -54,6 +54,10 @@ Item { d.currentItem.focus = true; d.currentItem.forceActiveFocus(); breadcrumbText.text = d.currentItem.title; + if (typeof bgNavBar !== "undefined") { + d.currentItem.y = bgNavBar.height; + d.currentItem.height -= bgNavBar.height; + } } function popSource() { From 45829574c21c18028b605f6ffe36577fa1849d02 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 15 Mar 2017 00:02:42 +0100 Subject: [PATCH 28/32] Allow the user to bring up the tablet when a message is open in the tablet --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 141b168f82..54398a4f07 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1625,9 +1625,10 @@ QString Application::getUserAgent() { void Application::toggleTabletUI() const { auto tabletScriptingInterface = DependencyManager::get(); + auto hmd = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); bool messageOpen = tablet->isMessageDialogOpen(); - if (!messageOpen) { + if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); } From cb4bd5838192c75a41081d40eec92f0ded824d73 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 15 Mar 2017 14:31:13 +1300 Subject: [PATCH 29/32] Load controller scripts in their own script engine --- scripts/defaultScripts.js | 32 ++++++++------- .../system/controllers/controllerScripts.js | 41 +++++++++++++++++++ 2 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 scripts/system/controllers/controllerScripts.js diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 27efab134a..aba2f35e6b 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var DEFAULT_SCRIPTS = [ +var DEFAULT_SCRIPTS_COMBINED = [ "system/progress.js", "system/away.js", "system/audio.js", @@ -27,17 +27,13 @@ var DEFAULT_SCRIPTS = [ "system/tablet-users.js", "system/selectAudioDevice.js", "system/notifications.js", - "system/controllers/squeezeHands.js", - "system/controllers/controllerDisplayManager.js", - "system/controllers/handControllerGrab.js", - "system/controllers/handControllerPointer.js", - "system/controllers/grab.js", - "system/controllers/teleport.js", - "system/controllers/toggleAdvancedMovementForHandControllers.js", "system/dialTone.js", "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js" ]; +var DEFAULT_SCRIPTS_SEPARATE = [ + "system/controllers/controllerScripts.js" +]; // add a menu item for debugging var MENU_CATEGORY = "Developer"; @@ -64,16 +60,24 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I }); } -function runDefaultsTogether() { - for (var j in DEFAULT_SCRIPTS) { - Script.include(DEFAULT_SCRIPTS[j]); +function loadSeparateDefaults() { + for (var i in DEFAULT_SCRIPTS_SEPARATE) { + Script.load(DEFAULT_SCRIPTS_SEPARATE[i]); } } -function runDefaultsSeparately() { - for (var i in DEFAULT_SCRIPTS) { - Script.load(DEFAULT_SCRIPTS[i]); +function runDefaultsTogether() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.include(DEFAULT_SCRIPTS_COMBINED[i]); } + loadSeparateDefaults(); +} + +function runDefaultsSeparately() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.load(DEFAULT_SCRIPTS_COMBINED[i]); + } + loadSeparateDefaults(); } // start all scripts diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js new file mode 100644 index 0000000000..df11a1e5be --- /dev/null +++ b/scripts/system/controllers/controllerScripts.js @@ -0,0 +1,41 @@ +"use strict"; + +// controllerScripts.js +// +// Created by David Rowe on 15 Mar 2017. +// Copyright 2017 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 +// + +var CONTOLLER_SCRIPTS = [ + "squeezeHands.js", + "controllerDisplayManager.js", + "handControllerGrab.js", + "handControllerPointer.js", + "grab.js", + "teleport.js", + "toggleAdvancedMovementForHandControllers.js", +]; + +var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; + + +function runDefaultsTogether() { + for (var j in CONTOLLER_SCRIPTS) { + Script.include(CONTOLLER_SCRIPTS[j]); + } +} + +function runDefaultsSeparately() { + for (var i in CONTOLLER_SCRIPTS) { + Script.load(CONTOLLER_SCRIPTS[i]); + } +} + +if (Menu.isOptionChecked(DEBUG_MENU_ITEM)) { + runDefaultsSeparately(); +} else { + runDefaultsTogether(); +} From 181c7da51a08ab860b00df0f80cfb7ec88373663 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Wed, 15 Mar 2017 14:55:53 +0100 Subject: [PATCH 30/32] Fixed warnings --- interface/src/ui/OctreeStatsProvider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp index 95092d4253..5f40b9916d 100644 --- a/interface/src/ui/OctreeStatsProvider.cpp +++ b/interface/src/ui/OctreeStatsProvider.cpp @@ -17,8 +17,8 @@ OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) : QObject(parent), _model(model) - , _averageUpdatesPerSecond(SAMPLES_PER_SECOND) , _statCount(0) + , _averageUpdatesPerSecond(SAMPLES_PER_SECOND) { //schedule updates connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData); From 816b0b5cfdc1beb2685da0272e7c18b52f142570 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 15 Mar 2017 09:02:32 -0800 Subject: [PATCH 31/32] unmangle merge --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8ed9cc23c7..668c732ca0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -760,7 +760,7 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, - toPointerButton(*event), toPointerButtons(*event)); + toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier); emit mouseDoublePressOnEntity(rayPickResult.entityID, pointerEvent); From b66ff1959543992073bcd12f3ccb423634a78fc8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 15 Mar 2017 09:51:30 -0800 Subject: [PATCH 32/32] prefer-avatar-finger-over-stylus setting now defaults to true --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1fc2eeaacf..040ee46bb8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -552,7 +552,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false; -const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; +const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = true; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) : QApplication(argc, argv),