From a7750501c6751473f8749befbf96c90c2158d883 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Dec 2016 18:58:11 -0800 Subject: [PATCH 001/118] first cut at adding view frustum support to avatar mixer --- assignment-client/src/avatars/AvatarMixer.cpp | 23 +++++++ assignment-client/src/avatars/AvatarMixer.h | 1 + .../src/avatars/AvatarMixerClientData.cpp | 7 ++ .../src/avatars/AvatarMixerClientData.h | 9 +++ interface/src/Application.cpp | 9 +++ interface/src/Application.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/shared/src/ViewFrustum.cpp | 67 +++++++++++++++++++ libraries/shared/src/ViewFrustum.h | 4 ++ 9 files changed, 123 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a37c9f7dd5..36952d4fd6 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -44,6 +44,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket"); packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); @@ -275,6 +276,16 @@ void AvatarMixer::broadcastAvatarData() { } // Not close enough to ignore nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID()); + + + // Also check to see if the other node is in our view + glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; + AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + if (!nodeData->otherAvatarInView(otherNodeBox)) { + qDebug() << "Avatar out of view!"; + return false; + } + return true; } }, @@ -448,6 +459,18 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { } } +void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->getOrCreateLinkedData(senderNode); + + if (senderNode->getLinkedData()) { + AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); + if (nodeData != nullptr) { + nodeData->readViewFrustumPacket(message->getMessage()); + } + } +} + void AvatarMixer::handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode) { auto nodeList = DependencyManager::get(); nodeList->updateNodeWithDataFromPacket(message, senderNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index f537cc9244..ba74b43308 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -34,6 +34,7 @@ public slots: void sendStatsPacket() override; private slots: + void handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index de843c7cea..3773bd723f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -57,6 +57,13 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe } } +void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { + _currentViewFrustumIsValid = true; + _currentViewFrustum.fromByteArray(message); + qDebug() << __FUNCTION__; + _currentViewFrustum.printDebugDetails(); +} + void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 20c1b6b647..85955804a9 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -27,6 +27,7 @@ #include #include #include +#include const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; @@ -87,6 +88,12 @@ public: void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); } void ignoreOther(SharedNodePointer self, SharedNodePointer other); + void readViewFrustumPacket(const QByteArray& message); + + bool otherAvatarInView(const AABox& otherAvatarBox) { + return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); + } + private: AvatarSharedPointer _avatar { new AvatarData() }; @@ -108,6 +115,8 @@ private: SimpleMovingAverage _avgOtherAvatarDataRate; std::unordered_set _radiusIgnoredOthers; + ViewFrustum _currentViewFrustum; + bool _currentViewFrustumIsValid { false }; }; #endif // hifi_AvatarMixerClientData_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4ec29b636b..8c3e3fad73 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4176,6 +4176,7 @@ void Application::update(float deltaTime) { if (DependencyManager::get()->shouldRenderEntities()) { queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions); } + sendAvatarViewFrustum(); _lastQueriedViewFrustum = _viewFrustum; } } @@ -4215,6 +4216,14 @@ void Application::update(float deltaTime) { AnimDebugDraw::getInstance().update(); } +void Application::sendAvatarViewFrustum() { + QByteArray viewFrustumByteArray = _viewFrustum.toByteArray(); + auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size()); + avatarPacket->write(viewFrustumByteArray); + + DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); +} + int Application::sendNackPackets() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 5ab94465cc..ea768b956b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -415,6 +415,7 @@ private: void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed); int sendNackPackets(); + void sendAvatarViewFrustum(); std::shared_ptr getMyAvatar() const; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index dbeb1e63b0..bc5ff6fe98 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -101,7 +101,8 @@ public: NodeKickRequest, NodeMuteRequest, RadiusIgnoreRequest, - LAST_PACKET_TYPE = RadiusIgnoreRequest + ViewFrustum, + LAST_PACKET_TYPE = ViewFrustum }; }; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 0a1a41ace1..ead8f8634e 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -130,6 +130,73 @@ const char* ViewFrustum::debugPlaneName (int plane) const { return "Unknown"; } +void ViewFrustum::fromByteArray(const QByteArray& input) { + + // From the wire! + glm::vec3 cameraPosition; + glm::quat cameraOrientation; + float cameraCenterRadius; + float cameraFov; + float cameraAspectRatio; + float cameraNearClip; + float cameraFarClip; + + const unsigned char* startPosition = reinterpret_cast(input.constData()); + const unsigned char* sourceBuffer = startPosition; + + // camera details + memcpy(&cameraPosition, sourceBuffer, sizeof(cameraPosition)); + sourceBuffer += sizeof(cameraPosition); + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, cameraOrientation); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &cameraFov); + sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, cameraAspectRatio); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraNearClip); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraFarClip); + memcpy(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius)); + sourceBuffer += sizeof(cameraCenterRadius); + + setPosition(cameraPosition); + setOrientation(cameraOrientation); + setCenterRadius(cameraCenterRadius); + + // Also make sure it's got the correct lens details from the camera + const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f; + float originalFOV = cameraFov; + float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; + + if (0.0f != cameraAspectRatio && + 0.0f != cameraNearClip && + 0.0f != cameraFarClip && + cameraNearClip != cameraFarClip) { + setProjection(glm::perspective( + glm::radians(wideFOV), // hack + cameraAspectRatio, + cameraNearClip, + cameraFarClip)); + } +} + + +QByteArray ViewFrustum::toByteArray() { + static const int LARGE_ENOUGH = 1024; + QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0); + unsigned char* destinationBuffer = reinterpret_cast(viewFrustumDataByteArray.data()); + unsigned char* startPosition = destinationBuffer; + + // camera details + memcpy(destinationBuffer, &_position, sizeof(_position)); + destinationBuffer += sizeof(_position); + destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView); + destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip); + memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius)); + destinationBuffer += sizeof(_centerSphereRadius); + + return viewFrustumDataByteArray.left(destinationBuffer - startPosition); +} + ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const { // only check against frustum ViewFrustum::intersection result = INSIDE; diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 091aff7a92..9a6cb9ab68 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -139,6 +139,10 @@ public: const ::Plane* getPlanes() const { return _planes; } void invalidate(); // causes all reasonable intersection tests to fail + + QByteArray toByteArray(); + void fromByteArray(const QByteArray& input); + private: glm::mat4 _view; glm::mat4 _projection; From 7189f766a2e61fcf6925a65840a2ca8d1454c598 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Dec 2016 20:56:41 -0800 Subject: [PATCH 002/118] more hacking --- assignment-client/src/avatars/AvatarMixer.cpp | 5 ++++- .../src/avatars/AvatarMixerClientData.cpp | 11 +++++++++-- assignment-client/src/avatars/AvatarMixerClientData.h | 5 ++--- libraries/shared/src/ViewFrustum.cpp | 2 ++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 36952d4fd6..5f031e537e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -281,9 +281,12 @@ void AvatarMixer::broadcastAvatarData() { // Also check to see if the other node is in our view glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + if (!nodeData->otherAvatarInView(otherNodeBox)) { - qDebug() << "Avatar out of view!"; + //qDebug() << "Avatar out of view!"; return false; + } else { + //qDebug() << "Avatar in view!"; } return true; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 3773bd723f..63a1b108b8 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -60,10 +60,17 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { _currentViewFrustumIsValid = true; _currentViewFrustum.fromByteArray(message); - qDebug() << __FUNCTION__; - _currentViewFrustum.printDebugDetails(); } +bool AvatarMixerClientData::otherAvatarInView(const glm::vec3& otherAvatar) { + return !_currentViewFrustumIsValid || _currentViewFrustum.pointIntersectsFrustum(otherAvatar); +} + +bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { + return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); +} + + void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 85955804a9..b7b7b16ea1 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -90,9 +90,8 @@ public: void readViewFrustumPacket(const QByteArray& message); - bool otherAvatarInView(const AABox& otherAvatarBox) { - return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); - } + bool otherAvatarInView(const AABox& otherAvatarBox); + bool otherAvatarInView(const glm::vec3& otherAvatar); private: AvatarSharedPointer _avatar { new AvatarData() }; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index ead8f8634e..b4ed462639 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -173,6 +173,8 @@ void ViewFrustum::fromByteArray(const QByteArray& input) { cameraAspectRatio, cameraNearClip, cameraFarClip)); + + calculate(); } } From 229017928193aca520d4d592bd0d4cde8c5a4430 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 11:47:45 -0800 Subject: [PATCH 003/118] add compression and minimal data flags to AvatarData --- interface/src/avatar/MyAvatar.cpp | 6 +- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 421 +++++++++++++++------------ libraries/avatars/src/AvatarData.h | 9 +- 4 files changed, 250 insertions(+), 188 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c56d255201..50517b706c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -227,7 +227,7 @@ void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() } -QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); _globalBoundingBoxCorner.x = _characterController.getCapsuleRadius(); @@ -238,12 +238,12 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getPosition(); setPosition(getSkeletonPosition()); - QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll); + QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed); // copy the correct position back setPosition(oldPosition); return array; } - return AvatarData::toByteArray(cullSmallChanges, sendAll); + return AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed); } void MyAvatar::centerBody() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f081ec533b..609e93f616 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -333,7 +333,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges, bool sendAll) override; + QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) override; void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f81a1169af..ac378276fd 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -181,7 +181,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition()); } -QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { +QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -199,208 +199,242 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); unsigned char* startPosition = destinationBuffer; - auto header = reinterpret_cast(destinationBuffer); - header->position[0] = getLocalPosition().x; - header->position[1] = getLocalPosition().y; - header->position[2] = getLocalPosition().z; - header->globalPosition[0] = _globalPosition.x; - header->globalPosition[1] = _globalPosition.y; - header->globalPosition[2] = _globalPosition.z; - header->globalBoundingBoxCorner[0] = getPosition().x - _globalBoundingBoxCorner.x; - header->globalBoundingBoxCorner[1] = getPosition().y - _globalBoundingBoxCorner.y; - header->globalBoundingBoxCorner[2] = getPosition().z - _globalBoundingBoxCorner.z; - - glm::vec3 bodyEulerAngles = glm::degrees(safeEulerAngles(getLocalOrientation())); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z); - packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale()); - header->lookAtPosition[0] = _headData->_lookAtPosition.x; - header->lookAtPosition[1] = _headData->_lookAtPosition.y; - header->lookAtPosition[2] = _headData->_lookAtPosition.z; - header->audioLoudness = _headData->_audioLoudness; - - glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); - packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); - glm::vec3 scale = extractScale(sensorToWorldMatrix); - packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); - header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; - header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; - header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; - - setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); - // hand state - bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; - setSemiNibbleAt(header->flags, HAND_STATE_START_BIT, _handState & ~IS_FINGER_POINTING_FLAG); - if (isFingerPointing) { - setAtBit(header->flags, HAND_STATE_FINGER_POINTING_BIT); - } - // faceshift state - if (_headData->_isFaceTrackerConnected) { - setAtBit(header->flags, IS_FACESHIFT_CONNECTED); - } - // eye tracker state - if (_headData->_isEyeTrackerConnected) { - setAtBit(header->flags, IS_EYE_TRACKER_CONNECTED); - } - // referential state - QUuid parentID = getParentID(); - if (!parentID.isNull()) { - setAtBit(header->flags, HAS_REFERENTIAL); - } - destinationBuffer += sizeof(AvatarDataPacket::Header); - - if (!parentID.isNull()) { - auto parentInfo = reinterpret_cast(destinationBuffer); - QByteArray referentialAsBytes = parentID.toRfc4122(); - memcpy(parentInfo->parentUUID, referentialAsBytes.data(), referentialAsBytes.size()); - parentInfo->parentJointIndex = _parentJointIndex; - destinationBuffer += sizeof(AvatarDataPacket::ParentInfo); + // Leading flags, to indicate how much data is actually included in the packet... + UINT8 packetStateFlags = 0; + if (sendMinimum) { + setAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); } - // If it is connected, pack up the data - if (_headData->_isFaceTrackerConnected) { - auto faceTrackerInfo = reinterpret_cast(destinationBuffer); - - faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; - faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; - faceTrackerInfo->averageLoudness = _headData->_averageLoudness; - faceTrackerInfo->browAudioLift = _headData->_browAudioLift; - faceTrackerInfo->numBlendshapeCoefficients = _headData->_blendshapeCoefficients.size(); - destinationBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - - // followed by a variable number of float coefficients - memcpy(destinationBuffer, _headData->_blendshapeCoefficients.data(), _headData->_blendshapeCoefficients.size() * sizeof(float)); - destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); + // do we need this, or do we just always compress it? + if (compressed) { + setAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); } + memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); + destinationBuffer += sizeof(packetStateFlags); - QReadLocker readLock(&_jointDataLock); + if (!sendMinimum) { + auto header = reinterpret_cast(destinationBuffer); + header->position[0] = getLocalPosition().x; + header->position[1] = getLocalPosition().y; + header->position[2] = getLocalPosition().z; + header->globalPosition[0] = _globalPosition.x; + header->globalPosition[1] = _globalPosition.y; + header->globalPosition[2] = _globalPosition.z; + header->globalBoundingBoxCorner[0] = getPosition().x - _globalBoundingBoxCorner.x; + header->globalBoundingBoxCorner[1] = getPosition().y - _globalBoundingBoxCorner.y; + header->globalBoundingBoxCorner[2] = getPosition().z - _globalBoundingBoxCorner.z; - // joint rotation data - *destinationBuffer++ = _jointData.size(); - unsigned char* validityPosition = destinationBuffer; - unsigned char validity = 0; - int validityBit = 0; + glm::vec3 bodyEulerAngles = glm::degrees(safeEulerAngles(getLocalOrientation())); + packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y); + packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x); + packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z); + packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale()); + header->lookAtPosition[0] = _headData->_lookAtPosition.x; + header->lookAtPosition[1] = _headData->_lookAtPosition.y; + header->lookAtPosition[2] = _headData->_lookAtPosition.z; + header->audioLoudness = _headData->_audioLoudness; - #ifdef WANT_DEBUG - int rotationSentCount = 0; - unsigned char* beforeRotations = destinationBuffer; - #endif + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); + glm::vec3 scale = extractScale(sensorToWorldMatrix); + packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); + header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; + header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; + header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; - _lastSentJointData.resize(_jointData.size()); + setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); + // hand state + bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; + setSemiNibbleAt(header->flags, HAND_STATE_START_BIT, _handState & ~IS_FINGER_POINTING_FLAG); + if (isFingerPointing) { + setAtBit(header->flags, HAND_STATE_FINGER_POINTING_BIT); + } + // faceshift state + if (_headData->_isFaceTrackerConnected) { + setAtBit(header->flags, IS_FACESHIFT_CONNECTED); + } + // eye tracker state + if (_headData->_isEyeTrackerConnected) { + setAtBit(header->flags, IS_EYE_TRACKER_CONNECTED); + } + // referential state + QUuid parentID = getParentID(); + if (!parentID.isNull()) { + setAtBit(header->flags, HAS_REFERENTIAL); + } + destinationBuffer += sizeof(AvatarDataPacket::Header); - for (int i=0; i < _jointData.size(); i++) { - const JointData& data = _jointData[i]; - if (sendAll || _lastSentJointData[i].rotation != data.rotation) { - if (sendAll || - !cullSmallChanges || - fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - if (data.rotationSet) { - validity |= (1 << validityBit); - #ifdef WANT_DEBUG - rotationSentCount++; - #endif + if (!parentID.isNull()) { + auto parentInfo = reinterpret_cast(destinationBuffer); + QByteArray referentialAsBytes = parentID.toRfc4122(); + memcpy(parentInfo->parentUUID, referentialAsBytes.data(), referentialAsBytes.size()); + parentInfo->parentJointIndex = _parentJointIndex; + destinationBuffer += sizeof(AvatarDataPacket::ParentInfo); + } + + // If it is connected, pack up the data + if (_headData->_isFaceTrackerConnected) { + auto faceTrackerInfo = reinterpret_cast(destinationBuffer); + + faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; + faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; + faceTrackerInfo->averageLoudness = _headData->_averageLoudness; + faceTrackerInfo->browAudioLift = _headData->_browAudioLift; + faceTrackerInfo->numBlendshapeCoefficients = _headData->_blendshapeCoefficients.size(); + destinationBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); + + // followed by a variable number of float coefficients + memcpy(destinationBuffer, _headData->_blendshapeCoefficients.data(), _headData->_blendshapeCoefficients.size() * sizeof(float)); + destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); + } + + QReadLocker readLock(&_jointDataLock); + + // joint rotation data + *destinationBuffer++ = _jointData.size(); + unsigned char* validityPosition = destinationBuffer; + unsigned char validity = 0; + int validityBit = 0; + + #ifdef WANT_DEBUG + int rotationSentCount = 0; + unsigned char* beforeRotations = destinationBuffer; + #endif + + _lastSentJointData.resize(_jointData.size()); + + for (int i=0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; + if (sendAll || _lastSentJointData[i].rotation != data.rotation) { + if (sendAll || + !cullSmallChanges || + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { + if (data.rotationSet) { + validity |= (1 << validityBit); + #ifdef WANT_DEBUG + rotationSentCount++; + #endif + } } } - } - if (++validityBit == BITS_IN_BYTE) { - *destinationBuffer++ = validity; - validityBit = validity = 0; - } - } - if (validityBit != 0) { - *destinationBuffer++ = validity; - } - - validityBit = 0; - validity = *validityPosition++; - for (int i = 0; i < _jointData.size(); i ++) { - const JointData& data = _jointData[i]; - if (validity & (1 << validityBit)) { - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); - } - if (++validityBit == BITS_IN_BYTE) { - validityBit = 0; - validity = *validityPosition++; - } - } - - - // joint translation data - validityPosition = destinationBuffer; - validity = 0; - validityBit = 0; - - #ifdef WANT_DEBUG - int translationSentCount = 0; - unsigned char* beforeTranslations = destinationBuffer; - #endif - - float maxTranslationDimension = 0.0; - for (int i=0; i < _jointData.size(); i++) { - const JointData& data = _jointData[i]; - if (sendAll || _lastSentJointData[i].translation != data.translation) { - if (sendAll || - !cullSmallChanges || - glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { - if (data.translationSet) { - validity |= (1 << validityBit); - #ifdef WANT_DEBUG - translationSentCount++; - #endif - maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); - maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); - } + if (++validityBit == BITS_IN_BYTE) { + *destinationBuffer++ = validity; + validityBit = validity = 0; } } - if (++validityBit == BITS_IN_BYTE) { + if (validityBit != 0) { *destinationBuffer++ = validity; - validityBit = validity = 0; } - } - if (validityBit != 0) { - *destinationBuffer++ = validity; - } - - validityBit = 0; - validity = *validityPosition++; - for (int i = 0; i < _jointData.size(); i ++) { - const JointData& data = _jointData[i]; - if (validity & (1 << validityBit)) { - destinationBuffer += - packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); + validityBit = 0; + validity = *validityPosition++; + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[i]; + if (validity & (1 << validityBit)) { + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); + } + if (++validityBit == BITS_IN_BYTE) { + validityBit = 0; + validity = *validityPosition++; + } } - if (++validityBit == BITS_IN_BYTE) { - validityBit = 0; - validity = *validityPosition++; + + + // joint translation data + validityPosition = destinationBuffer; + validity = 0; + validityBit = 0; + + #ifdef WANT_DEBUG + int translationSentCount = 0; + unsigned char* beforeTranslations = destinationBuffer; + #endif + + float maxTranslationDimension = 0.0; + for (int i=0; i < _jointData.size(); i++) { + const JointData& data = _jointData[i]; + if (sendAll || _lastSentJointData[i].translation != data.translation) { + if (sendAll || + !cullSmallChanges || + glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { + if (data.translationSet) { + validity |= (1 << validityBit); + #ifdef WANT_DEBUG + translationSentCount++; + #endif + maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); + } + } + } + if (++validityBit == BITS_IN_BYTE) { + *destinationBuffer++ = validity; + validityBit = validity = 0; + } } + + if (validityBit != 0) { + *destinationBuffer++ = validity; + } + + validityBit = 0; + validity = *validityPosition++; + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[i]; + if (validity & (1 << validityBit)) { + destinationBuffer += + packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); + } + if (++validityBit == BITS_IN_BYTE) { + validityBit = 0; + validity = *validityPosition++; + } + } + + // faux joints + Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation()); + destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(), + TRANSLATION_COMPRESSION_RADIX); + Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); + destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation()); + destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), + TRANSLATION_COMPRESSION_RADIX); + + #ifdef WANT_DEBUG + if (sendAll) { + qDebug() << "AvatarData::toByteArray" << cullSmallChanges << sendAll + << "rotations:" << rotationSentCount << "translations:" << translationSentCount + << "largest:" << maxTranslationDimension + << "size:" + << (beforeRotations - startPosition) << "+" + << (beforeTranslations - beforeRotations) << "+" + << (destinationBuffer - beforeTranslations) << "=" + << (destinationBuffer - startPosition); + } + #endif } - // faux joints - Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation()); - destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(), - TRANSLATION_COMPRESSION_RADIX); - Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix()); - destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation()); - destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), - TRANSLATION_COMPRESSION_RADIX); + // NOTE: first byte of array should always be uncompressed - #ifdef WANT_DEBUG - if (sendAll) { - qDebug() << "AvatarData::toByteArray" << cullSmallChanges << sendAll - << "rotations:" << rotationSentCount << "translations:" << translationSentCount - << "largest:" << maxTranslationDimension - << "size:" - << (beforeRotations - startPosition) << "+" - << (beforeTranslations - beforeRotations) << "+" - << (destinationBuffer - beforeTranslations) << "=" - << (destinationBuffer - startPosition); + if (compressed) { + static const int SKIP_PACKET_FLAGS = 1; + static const int COMPRESSION_LEVEL = 9; + QByteArray uncompressedPortion = avatarDataByteArray.mid(SKIP_PACKET_FLAGS, + (destinationBuffer - startPosition) - SKIP_PACKET_FLAGS); + + QByteArray compressedPortion = qCompress(uncompressedPortion, COMPRESSION_LEVEL); + QByteArray flagsAndCompressed; + flagsAndCompressed.append(packetStateFlags); + flagsAndCompressed.append(compressedPortion); + + //qDebug() << __FUNCTION__ << "compressing data was:" << (uncompressedPortion.size() + SKIP_PACKET_FLAGS) << "now:" << flagsAndCompressed.size(); + + return flagsAndCompressed; } - #endif + // entire buffer is uncompressed return avatarDataByteArray.left(destinationBuffer - startPosition); } @@ -474,9 +508,30 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _headData = new HeadData(this); } + UINT8 packetStateFlags = buffer.at(0); + bool minimumSent = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); + bool packetIsCompressed = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); + + // if this is the minimum, then it only includes the flags + if (minimumSent) { + int numBytesRead = sizeof(packetStateFlags); + _averageBytesReceived.updateAverage(numBytesRead); + return numBytesRead; + } + + QByteArray uncompressBuffer; const unsigned char* startPosition = reinterpret_cast(buffer.data()); const unsigned char* endPosition = startPosition + buffer.size(); const unsigned char* sourceBuffer = startPosition; + + if (packetIsCompressed) { + uncompressBuffer = qUncompress(buffer.right(buffer.size() - sizeof(packetStateFlags))); + startPosition = reinterpret_cast(uncompressBuffer.data()); + endPosition = startPosition + uncompressBuffer.size(); + sourceBuffer = startPosition; + //qDebug() << __FUNCTION__ << "uncompressing compressed data was:" << buffer.size() << "now:" << uncompressBuffer.size(); + } + quint64 now = usecTimestampNow(); PACKET_READ_CHECK(Header, sizeof(AvatarDataPacket::Header)); @@ -679,8 +734,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { #ifdef WANT_DEBUG if (numValidJointRotations > 15) { qDebug() << "RECEIVING -- rotations:" << numValidJointRotations - << "translations:" << numValidJointTranslations - << "size:" << (int)(sourceBuffer - startPosition); + << "translations:" << numValidJointTranslations + << "size:" << (int)(sourceBuffer - startPosition); } #endif diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b34b912f14..49f9336aeb 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -106,6 +106,13 @@ const char LEFT_HAND_POINTING_FLAG = 1; const char RIGHT_HAND_POINTING_FLAG = 2; const char IS_FINGER_POINTING_FLAG = 4; +// AvatarData state flags - we store the details about the packet encoding in the first byte, +// before the "header" structure +const char AVATARDATA_FLAGS_MINIMUM = 0; +const char AVATARDATA_FLAGS_COMPRESSED = 1; + + + static const float MAX_AVATAR_SCALE = 1000.0f; static const float MIN_AVATAR_SCALE = .005f; @@ -201,7 +208,7 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll); + virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false, bool compressed = true); virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged From e57774f6306d0a1bd8e1b884e7a02a4677c73504 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 12:19:50 -0800 Subject: [PATCH 004/118] fix unix/mac build --- libraries/avatars/src/AvatarData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ac378276fd..34e9838284 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -200,7 +200,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen unsigned char* startPosition = destinationBuffer; // Leading flags, to indicate how much data is actually included in the packet... - UINT8 packetStateFlags = 0; + uint8_t packetStateFlags = 0; if (sendMinimum) { setAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); } @@ -508,7 +508,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _headData = new HeadData(this); } - UINT8 packetStateFlags = buffer.at(0); + uint8_t packetStateFlags = buffer.at(0); bool minimumSent = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); bool packetIsCompressed = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); From daccb708193111b41c173a56cecc10c3213de3fe Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 13:18:09 -0800 Subject: [PATCH 005/118] disable compression by default --- libraries/avatars/src/AvatarData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 49f9336aeb..c985559913 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -208,7 +208,7 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false, bool compressed = true); + virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false, bool compressed = false); virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged From 2443b2d6d97635bc45b76d45596192f68b197a50 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 17:35:02 -0800 Subject: [PATCH 006/118] duh --- libraries/avatars/src/AvatarData.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 34e9838284..f29d2d9df0 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -209,10 +209,13 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen if (compressed) { setAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); } + memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); if (!sendMinimum) { + //qDebug() << __FUNCTION__ << "not minimum... sending actual content!!"; + auto header = reinterpret_cast(destinationBuffer); header->position[0] = getLocalPosition().x; header->position[1] = getLocalPosition().y; @@ -514,15 +517,18 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // if this is the minimum, then it only includes the flags if (minimumSent) { + //qDebug() << __FUNCTION__ << "minimum... not expecting actual content!!"; + int numBytesRead = sizeof(packetStateFlags); _averageBytesReceived.updateAverage(numBytesRead); return numBytesRead; } + //qDebug() << __FUNCTION__ << "NOT minimum... expecting actual content!!"; QByteArray uncompressBuffer; const unsigned char* startPosition = reinterpret_cast(buffer.data()); const unsigned char* endPosition = startPosition + buffer.size(); - const unsigned char* sourceBuffer = startPosition; + const unsigned char* sourceBuffer = startPosition + sizeof(packetStateFlags); // skip the flags!! if (packetIsCompressed) { uncompressBuffer = qUncompress(buffer.right(buffer.size() - sizeof(packetStateFlags))); From c8fb467579104955fce9bcce5cad78a4112413c7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 18:17:11 -0800 Subject: [PATCH 007/118] send minimum data for out of view avatars --- assignment-client/src/avatars/AvatarMixer.cpp | 21 ++++-------- libraries/avatars/src/AvatarData.cpp | 32 +++++++++++++------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 5f031e537e..1fe71cf59e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -276,19 +276,6 @@ void AvatarMixer::broadcastAvatarData() { } // Not close enough to ignore nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID()); - - - // Also check to see if the other node is in our view - glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; - AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - - if (!nodeData->otherAvatarInView(otherNodeBox)) { - //qDebug() << "Avatar out of view!"; - return false; - } else { - //qDebug() << "Avatar in view!"; - } - return true; } }, @@ -368,9 +355,15 @@ void AvatarMixer::broadcastAvatarData() { // start a new segment in the PacketList for this avatar avatarPacketList->startSegment(); + // determine if avatar is in view, to determine how much data to include... + glm::vec3 otherNodeBoxScale = (otherNodeData->getPosition() - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + + bool sendMinimumForOutOfView = !nodeData->otherAvatarInView(otherNodeBox); + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += - avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO)); + avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO), sendMinimumForOutOfView); avatarPacketList->endSegment(); }); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f29d2d9df0..00db9ae943 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -213,7 +213,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); - if (!sendMinimum) { + if (sendMinimum) { + memcpy(destinationBuffer, &_globalPosition, sizeof(_globalPosition)); + destinationBuffer += sizeof(_globalPosition); + qDebug() << __FUNCTION__ << "minimum... included global position!!"; + } + else { //qDebug() << __FUNCTION__ << "not minimum... sending actual content!!"; auto header = reinterpret_cast(destinationBuffer); @@ -515,14 +520,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { bool minimumSent = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); bool packetIsCompressed = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); - // if this is the minimum, then it only includes the flags - if (minimumSent) { - //qDebug() << __FUNCTION__ << "minimum... not expecting actual content!!"; - - int numBytesRead = sizeof(packetStateFlags); - _averageBytesReceived.updateAverage(numBytesRead); - return numBytesRead; - } //qDebug() << __FUNCTION__ << "NOT minimum... expecting actual content!!"; QByteArray uncompressBuffer; @@ -538,6 +535,20 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { //qDebug() << __FUNCTION__ << "uncompressing compressed data was:" << buffer.size() << "now:" << uncompressBuffer.size(); } + // if this is the minimum, then it only includes the flags + if (minimumSent) { + //qDebug() << __FUNCTION__ << "minimum... not expecting actual content!!"; + + memcpy(&_globalPosition, sourceBuffer, sizeof(_globalPosition)); + sourceBuffer += sizeof(_globalPosition); + int numBytesRead = (sourceBuffer - startPosition) + sizeof(packetStateFlags); + _averageBytesReceived.updateAverage(numBytesRead); + + qDebug() << __FUNCTION__ << "minimum... included global position!! numBytesRead:" << numBytesRead; + + return numBytesRead; + } + quint64 now = usecTimestampNow(); PACKET_READ_CHECK(Header, sizeof(AvatarDataPacket::Header)); @@ -750,6 +761,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerRightHandMatrixCache); int numBytesRead = sourceBuffer - startPosition; + if (packetIsCompressed) { + numBytesRead += sizeof(packetStateFlags); + } _averageBytesReceived.updateAverage(numBytesRead); return numBytesRead; } From 439434b300d5d488dd128001c026f520824536a0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Dec 2016 20:24:52 -0800 Subject: [PATCH 008/118] fixes --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 1fe71cf59e..b394673702 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -363,7 +363,7 @@ void AvatarMixer::broadcastAvatarData() { numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += - avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO), sendMinimumForOutOfView); + avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO, sendMinimumForOutOfView)); avatarPacketList->endSegment(); }); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 00db9ae943..3f61c5f8aa 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -217,8 +217,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen memcpy(destinationBuffer, &_globalPosition, sizeof(_globalPosition)); destinationBuffer += sizeof(_globalPosition); qDebug() << __FUNCTION__ << "minimum... included global position!!"; - } - else { + } else { //qDebug() << __FUNCTION__ << "not minimum... sending actual content!!"; auto header = reinterpret_cast(destinationBuffer); @@ -541,10 +540,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { memcpy(&_globalPosition, sourceBuffer, sizeof(_globalPosition)); sourceBuffer += sizeof(_globalPosition); - int numBytesRead = (sourceBuffer - startPosition) + sizeof(packetStateFlags); + int numBytesRead = (sourceBuffer - startPosition); _averageBytesReceived.updateAverage(numBytesRead); - qDebug() << __FUNCTION__ << "minimum... included global position!! numBytesRead:" << numBytesRead; + //qDebug() << __FUNCTION__ << "minimum... included global position!! numBytesRead:" << numBytesRead; return numBytesRead; } From ed19b4b592914c061d22f2ad4e42bd277fd9d24a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Dec 2016 14:13:38 -0800 Subject: [PATCH 009/118] remove compression at the per avatar data buffer for now, since it gave no benefit --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 6 +-- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 46 +------------------ libraries/avatars/src/AvatarData.h | 4 +- 5 files changed, 6 insertions(+), 53 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b394673702..90a8f65c9c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -358,7 +358,6 @@ void AvatarMixer::broadcastAvatarData() { // determine if avatar is in view, to determine how much data to include... glm::vec3 otherNodeBoxScale = (otherNodeData->getPosition() - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - bool sendMinimumForOutOfView = !nodeData->otherAvatarInView(otherNodeBox); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 50517b706c..1b1bd9f30d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -227,7 +227,7 @@ void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() } -QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); _globalBoundingBoxCorner.x = _characterController.getCapsuleRadius(); @@ -238,12 +238,12 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendM // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getPosition(); setPosition(getSkeletonPosition()); - QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed); + QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum); // copy the correct position back setPosition(oldPosition); return array; } - return AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed); + return AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum); } void MyAvatar::centerBody() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 609e93f616..14770fa14f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -333,7 +333,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) override; + QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) override; void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3f61c5f8aa..135cf48fa3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -181,7 +181,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition()); } -QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) { +QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -205,11 +205,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen setAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); } - // do we need this, or do we just always compress it? - if (compressed) { - setAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); - } - memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); @@ -423,25 +418,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen #endif } - // NOTE: first byte of array should always be uncompressed - - if (compressed) { - static const int SKIP_PACKET_FLAGS = 1; - static const int COMPRESSION_LEVEL = 9; - QByteArray uncompressedPortion = avatarDataByteArray.mid(SKIP_PACKET_FLAGS, - (destinationBuffer - startPosition) - SKIP_PACKET_FLAGS); - - QByteArray compressedPortion = qCompress(uncompressedPortion, COMPRESSION_LEVEL); - QByteArray flagsAndCompressed; - flagsAndCompressed.append(packetStateFlags); - flagsAndCompressed.append(compressedPortion); - - //qDebug() << __FUNCTION__ << "compressing data was:" << (uncompressedPortion.size() + SKIP_PACKET_FLAGS) << "now:" << flagsAndCompressed.size(); - - return flagsAndCompressed; - } - - // entire buffer is uncompressed return avatarDataByteArray.left(destinationBuffer - startPosition); } @@ -517,34 +493,17 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { uint8_t packetStateFlags = buffer.at(0); bool minimumSent = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM); - bool packetIsCompressed = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED); - //qDebug() << __FUNCTION__ << "NOT minimum... expecting actual content!!"; - - QByteArray uncompressBuffer; const unsigned char* startPosition = reinterpret_cast(buffer.data()); const unsigned char* endPosition = startPosition + buffer.size(); const unsigned char* sourceBuffer = startPosition + sizeof(packetStateFlags); // skip the flags!! - if (packetIsCompressed) { - uncompressBuffer = qUncompress(buffer.right(buffer.size() - sizeof(packetStateFlags))); - startPosition = reinterpret_cast(uncompressBuffer.data()); - endPosition = startPosition + uncompressBuffer.size(); - sourceBuffer = startPosition; - //qDebug() << __FUNCTION__ << "uncompressing compressed data was:" << buffer.size() << "now:" << uncompressBuffer.size(); - } - // if this is the minimum, then it only includes the flags if (minimumSent) { - //qDebug() << __FUNCTION__ << "minimum... not expecting actual content!!"; - memcpy(&_globalPosition, sourceBuffer, sizeof(_globalPosition)); sourceBuffer += sizeof(_globalPosition); int numBytesRead = (sourceBuffer - startPosition); _averageBytesReceived.updateAverage(numBytesRead); - - //qDebug() << __FUNCTION__ << "minimum... included global position!! numBytesRead:" << numBytesRead; - return numBytesRead; } @@ -760,9 +719,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerRightHandMatrixCache); int numBytesRead = sourceBuffer - startPosition; - if (packetIsCompressed) { - numBytesRead += sizeof(packetStateFlags); - } _averageBytesReceived.updateAverage(numBytesRead); return numBytesRead; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c985559913..dd23d5c86d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -109,8 +109,6 @@ const char IS_FINGER_POINTING_FLAG = 4; // AvatarData state flags - we store the details about the packet encoding in the first byte, // before the "header" structure const char AVATARDATA_FLAGS_MINIMUM = 0; -const char AVATARDATA_FLAGS_COMPRESSED = 1; - static const float MAX_AVATAR_SCALE = 1000.0f; @@ -208,7 +206,7 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false, bool compressed = false); + virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false); virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged From 6d7d3551d9cfc6cddb805e5576ed1ce0c6eb948f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Dec 2016 18:32:11 -0800 Subject: [PATCH 010/118] added some stats to the mixer fixed debug logging, improved timer precision --- assignment-client/src/avatars/AvatarMixer.cpp | 26 +++++++++++++++++++ assignment-client/src/avatars/AvatarMixer.h | 6 +++++ .../src/avatars/AvatarMixerClientData.cpp | 2 ++ .../src/avatars/AvatarMixerClientData.h | 7 +++++ libraries/avatars/src/AvatarData.cpp | 3 --- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 90a8f65c9c..53f7695095 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -72,6 +72,8 @@ const float IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. void AvatarMixer::broadcastAvatarData() { + _broadcastRate.increment(); + int idleTime = AVATAR_DATA_SEND_INTERVAL_MSECS; if (_lastFrameTimestamp.time_since_epoch().count() > 0) { @@ -160,6 +162,7 @@ void AvatarMixer::broadcastAvatarData() { return; } ++_sumListeners; + nodeData->resetInViewStats(); AvatarData& avatar = nodeData->getAvatar(); glm::vec3 myPosition = avatar.getClientGlobalPosition(); @@ -360,6 +363,12 @@ void AvatarMixer::broadcastAvatarData() { AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); bool sendMinimumForOutOfView = !nodeData->otherAvatarInView(otherNodeBox); + if (sendMinimumForOutOfView) { + nodeData->incrementAvatarOutOfView(); + } else { + nodeData->incrementAvatarInView(); + } + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO, sendMinimumForOutOfView)); @@ -391,6 +400,9 @@ void AvatarMixer::broadcastAvatarData() { // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so // that we can notice differences, next time around. + // + // FIXME - this seems suspicious, the code seems to consider all avatars, but not all avatars will + // have had their joints sent, so actually we should consider the time since they actually were sent???? nodeList->eachMatchingNode( [&](const SharedNodePointer& otherNode)->bool { if (!otherNode->getLinkedData()) { @@ -415,6 +427,18 @@ void AvatarMixer::broadcastAvatarData() { }); _lastFrameTimestamp = p_high_resolution_clock::now(); + +#ifdef WANT_DEBUG + auto sinceLastDebug = p_high_resolution_clock::now() - _lastDebugMessage; + auto sinceLastDebugUsecs = std::chrono::duration_cast(sinceLastDebug).count(); + quint64 DEBUG_INTERVAL = USECS_PER_SECOND * 5; + + if (sinceLastDebugUsecs > DEBUG_INTERVAL) { + qDebug() << "broadcast rate:" << _broadcastRate.rate() << "hz"; + _lastDebugMessage = p_high_resolution_clock::now(); + } +#endif + } void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { @@ -511,6 +535,7 @@ void AvatarMixer::sendStatsPacket() { statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; + statsObject["broadcast_loop_rate"] = _broadcastRate.rate(); QJsonObject avatarsObject; @@ -563,6 +588,7 @@ void AvatarMixer::run() { // setup the timer that will be fired on the broadcast thread _broadcastTimer = new QTimer; + _broadcastTimer->setTimerType(Qt::PreciseTimer); _broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); _broadcastTimer->moveToThread(&_broadcastThread); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index ba74b43308..9ac121d71c 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -15,6 +15,7 @@ #ifndef hifi_AvatarMixer_h #define hifi_AvatarMixer_h +#include #include #include @@ -65,6 +66,11 @@ private: float _domainMaximumScale { MAX_AVATAR_SCALE }; QTimer* _broadcastTimer = nullptr; + + RateCounter<> _broadcastRate; + p_high_resolution_clock::time_point _lastDebugMessage; + + }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 63a1b108b8..162ef2c462 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -84,4 +84,6 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar->getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; jsonObject["av_data_receive_rate"] = _avatar->getReceiveRate(); + jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView; + jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView; } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index b7b7b16ea1..e6a91e28e2 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -93,6 +93,10 @@ public: bool otherAvatarInView(const AABox& otherAvatarBox); bool otherAvatarInView(const glm::vec3& otherAvatar); + void resetInViewStats() { _recentOtherAvatarsInView = _recentOtherAvatarsOutOfView = 0; } + void incrementAvatarInView() { _recentOtherAvatarsInView++; } + void incrementAvatarOutOfView() { _recentOtherAvatarsOutOfView++; } + private: AvatarSharedPointer _avatar { new AvatarData() }; @@ -116,6 +120,9 @@ private: std::unordered_set _radiusIgnoredOthers; ViewFrustum _currentViewFrustum; bool _currentViewFrustumIsValid { false }; + + int _recentOtherAvatarsInView { 0 }; + int _recentOtherAvatarsOutOfView { 0 }; }; #endif // hifi_AvatarMixerClientData_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 135cf48fa3..ec8d589c23 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -211,10 +211,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sen if (sendMinimum) { memcpy(destinationBuffer, &_globalPosition, sizeof(_globalPosition)); destinationBuffer += sizeof(_globalPosition); - qDebug() << __FUNCTION__ << "minimum... included global position!!"; } else { - //qDebug() << __FUNCTION__ << "not minimum... sending actual content!!"; - auto header = reinterpret_cast(destinationBuffer); header->position[0] = getLocalPosition().x; header->position[1] = getLocalPosition().y; From 49fd4216de769c44d9756c9d0fd3ee60ba86c34f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 16 Dec 2016 14:15:20 -0800 Subject: [PATCH 011/118] sessionDisplayName set at server and passed to all clients (including oneself) --- assignment-client/src/avatars/AvatarMixer.cpp | 64 +++++++++++++++---- assignment-client/src/avatars/AvatarMixer.h | 4 ++ .../src/avatars/AvatarMixerClientData.h | 8 +++ interface/src/avatar/Avatar.h | 5 ++ libraries/avatars/src/AvatarData.cpp | 6 +- libraries/avatars/src/AvatarData.h | 9 +++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 8 files changed, 83 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a37c9f7dd5..ca7f931067 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,6 @@ #include #include -#include "AvatarMixerClientData.h" #include "AvatarMixer.h" const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; @@ -67,6 +67,20 @@ AvatarMixer::~AvatarMixer() { // assuming 60 htz update rate. const float IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; +void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { + QByteArray individualData = nodeData->getAvatar().identityByteArray(); + + auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); + + individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); + + identityPacket->write(individualData); + + DependencyManager::get()->sendPacket(std::move(identityPacket), *destinationNode); + + ++_sumIdentityPackets; +} + // NOTE: some additional optimizations to consider. // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. @@ -227,6 +241,27 @@ void AvatarMixer::broadcastAvatarData() { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + if (avatar.getSessionDisplayName().isEmpty() && // We haven't set it yet... + nodeData->getReceivedIdentity()) { // ... but we have processed identity (with possible displayName). + QString baseName = avatar.getDisplayName().trimmed(); + const QRegularExpression curses{ "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?). + baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk. + const QRegularExpression trailingDigits{ "\\s*_\\d+$" }; // whitespace "_123" + baseName = baseName.remove(trailingDigits); + if (baseName.isEmpty()) { + baseName = "anonymous"; + } + + QPair& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want. + int& highWater = soFar.first; + nodeData->setBaseDisplayName(baseName); + avatar.setSessionDisplayName((highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName); + highWater++; + soFar.second++; // refcount + nodeData->flagIdentityChange(); + sendIdentityPacket(nodeData, node); // Tell new node about its sessionUUID. Others will find out below. + } + // this is an AGENT we have received head data from // send back a packet with other active node data to this node nodeList->eachMatchingNode( @@ -295,17 +330,7 @@ void AvatarMixer::broadcastAvatarData() { || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); - - auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - - individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); - - identityPacket->write(individualData); - - nodeList->sendPacket(std::move(identityPacket), *node); - - ++_sumIdentityPackets; + sendIdentityPacket(otherNodeData, node); } AvatarData& otherAvatar = otherNodeData->getAvatar(); @@ -416,6 +441,16 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { && killedNode->getLinkedData()) { auto nodeList = DependencyManager::get(); + { // decrement sessionDisplayNames table and possibly remove + QMutexLocker nodeDataLocker(&killedNode->getLinkedData()->getMutex()); + AvatarMixerClientData* nodeData = dynamic_cast(killedNode->getLinkedData()); + const QString& baseDisplayName = nodeData->getBaseDisplayName(); + // No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case. + if (--_sessionDisplayNames[baseDisplayName].second <= 0) { + _sessionDisplayNames.remove(baseDisplayName); + } + } + // this was an avatar we were sending to other people // send a kill packet for it to our other nodes auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason)); @@ -467,8 +502,9 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); if (avatar.processAvatarIdentity(identity)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); - nodeData->flagIdentityChange(); - } + nodeData->flagIdentityChange(); + nodeData->setReceivedIdentity(); + } } } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index f537cc9244..3c39380b3a 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -18,6 +18,7 @@ #include #include +#include "AvatarMixerClientData.h" /// Handles assignments of type AvatarMixer - distribution of avatar data to various clients class AvatarMixer : public ThreadedAssignment { @@ -46,6 +47,7 @@ private slots: private: void broadcastAvatarData(); void parseDomainServerSettings(const QJsonObject& domainSettings); + void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); QThread _broadcastThread; @@ -64,6 +66,8 @@ private: float _domainMaximumScale { MAX_AVATAR_SCALE }; QTimer* _broadcastTimer = nullptr; + + QHash> _sessionDisplayNames; }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 20c1b6b647..538da4aab0 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -50,6 +50,8 @@ public: HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); } + bool getReceivedIdentity() const { return _gotIdentity; } + void setReceivedIdentity() { _gotIdentity = true; } void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } float getFullRateDistance() const { return _fullRateDistance; } @@ -87,6 +89,9 @@ public: void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); } void ignoreOther(SharedNodePointer self, SharedNodePointer other); + const QString& getBaseDisplayName() { return _baseDisplayName; } + void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; } + private: AvatarSharedPointer _avatar { new AvatarData() }; @@ -95,6 +100,7 @@ private: std::unordered_set _hasReceivedFirstPacketsFrom; HRCTime _identityChangeTimestamp; + bool _gotIdentity { false }; float _fullRateDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX; @@ -108,6 +114,8 @@ private: SimpleMovingAverage _avgOtherAvatarDataRate; std::unordered_set _radiusIgnoredOthers; + + QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. }; #endif // hifi_AvatarMixerClientData_h diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9f5ac392bc..dd0a9b2ab1 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -119,6 +119,7 @@ public: virtual void setAttachmentData(const QVector& attachmentData) override; void setShowDisplayName(bool showDisplayName); + virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op virtual int parseDataFromBuffer(const QByteArray& buffer) override; @@ -189,6 +190,10 @@ public slots: protected: friend class AvatarManager; + virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send. + QString _empty{}; + virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! + void setMotionState(AvatarMotionState* motionState); SkeletonModelPointer _skeletonModel; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f81a1169af..f1c5a3a5a4 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -984,7 +984,7 @@ QStringList AvatarData::getJointNames() const { void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) { QDataStream packetStream(data); - packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.avatarEntityData; + packetStream >> identityOut.uuid >> identityOut.skeletonModelURL >> identityOut.attachmentData >> identityOut.displayName >> identityOut.sessionDisplayName >> identityOut.avatarEntityData; } static const QUrl emptyURL(""); @@ -1006,6 +1006,7 @@ bool AvatarData::processAvatarIdentity(const Identity& identity) { setDisplayName(identity.displayName); hasIdentityChanged = true; } + maybeUpdateSessionDisplayNameFromTransport(identity.sessionDisplayName); if (identity.attachmentData != _attachmentData) { setAttachmentData(identity.attachmentData); @@ -1030,7 +1031,7 @@ QByteArray AvatarData::identityByteArray() { const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); _avatarEntitiesLock.withReadLock([&] { - identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << _avatarEntityData; + identityStream << getSessionUUID() << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() << _avatarEntityData; }); return identityData; @@ -1385,6 +1386,7 @@ static const QString JSON_AVATAR_HEAD = QStringLiteral("head"); static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); +// It isn't meaningful to persist sessionDisplayName. static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments"); static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b34b912f14..906d9b312a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -171,6 +171,9 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName) + // sessionDisplayName is sanitized, defaulted version displayName that is defined by the AvatarMixer rather than by Interface clients. + // The result is unique among all avatars present at the time. + Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) @@ -313,6 +316,7 @@ public: QUrl skeletonModelURL; QVector attachmentData; QString displayName; + QString sessionDisplayName; AvatarEntityMap avatarEntityData; }; @@ -325,9 +329,11 @@ public: const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } const QString& getDisplayName() const { return _displayName; } + const QString& getSessionDisplayName() const { return _sessionDisplayName; } virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); + virtual void setSessionDisplayName(const QString& sessionDisplayName) { _sessionDisplayName = sessionDisplayName; }; Q_INVOKABLE QVector getAttachmentData() const; Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); @@ -390,6 +396,8 @@ public slots: protected: glm::vec3 _handPosition; + virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } + virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer // Body scale float _targetScale; @@ -417,6 +425,7 @@ protected: QUrl _skeletonFBXURL; QVector _attachmentData; QString _displayName; + QString _sessionDisplayName { }; const QUrl& cannonicalSkeletonModelURL(const QUrl& empty); float _displayNameTargetAlpha; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 34a70f8da6..79776e2d54 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -53,7 +53,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::HasKillAvatarReason); + return static_cast(AvatarMixerPacketVersion::SessionDisplayName); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index dbeb1e63b0..778cce4c8d 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -203,7 +203,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AbsoluteSixByteRotations, SensorToWorldMat, HandControllerJoints, - HasKillAvatarReason + HasKillAvatarReason, + SessionDisplayName }; enum class DomainConnectRequestVersion : PacketVersion { From 70af96669c77493af72231dea637c6e6e831d061 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 16 Dec 2016 14:59:54 -0800 Subject: [PATCH 012/118] update pal --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 175418cd2e..61d3bb00e8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -117,7 +117,7 @@ function populateUserList() { AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { // sorting the identifiers is just an aid for debugging var avatar = AvatarList.getAvatar(id); var avatarPalDatum = { - displayName: avatar.displayName || ('anonymous ' + counter++), + displayName: avatar.sessionDisplayName, userName: "fakeAcct" + (id || "Me"), sessionId: id || '' }; From 70942925bc200da48196b5955d86156f12513d29 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 19 Dec 2016 10:32:17 -0800 Subject: [PATCH 013/118] Entity script for ambisonic sound emitter --- .../tutorials/entity_scripts/ambientSound.js | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 scripts/tutorials/entity_scripts/ambientSound.js diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js new file mode 100644 index 0000000000..a387f00c10 --- /dev/null +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -0,0 +1,101 @@ +// ambientSound.js +// +// This entity script will allow you to create an ambient sound that loops when a person is within a given +// range of this entity. Great way to add one or more ambisonic soundfields to your environment. +// +// In the userData section for the entity, add two values: +// userData.SoundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. +// userData.range should be an integer for the max distance away from the entity where the sound will be audible. +// userData.volume is the max volume at which the clip should play. +// +// Note: When you update the above values, you need to reload the entity script for it to see the changes. Also, +// remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is +// large enough to be loaded at the range you set, particularly for large ranges. +// +// 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 +// + +(function(){ + // This sample clip and range will be used if you don't add userData to the entity (see above) + var DEFAULT_RANGE = 100; + var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; + var DEFAULT_VOLUME = 1.0; + + var soundURL = DEFAULT_URL; + var range = DEFAULT_RANGE; + var maxVolume = DEFAULT_VOLUME; + var UPDATE_INTERVAL_MSECS = 100; + + var entity; + var ambientSound; + var center; + var soundPlaying = false; + var updateTimer = false; + + var WANT_DEBUG = false; + + function debugPrint(string) { + if (WANT_DEBUG) { + print(string); + } + } + + this.preload = function(entityID) { + // Load the sound and range from the entity userData fields, and note the position of the entity. + debugPrint("Ambient sound preload"); + entity = entityID; + var props = Entities.getEntityProperties(entityID, [ "userData" ]); + var data = JSON.parse(props.userData); + if (data.soundURL) { + soundURL = data.soundURL; + debugPrint("Read ambient sound URL: " + soundURL); + } + ambientSound = SoundCache.getSound(soundURL); + if (data.range) { + range = data.range; + debugPrint("Read ambient sound range: " + range); + } + if (data.volume) { + maxVolume = data.volume; + debugPrint("Read ambient sound volume: " + maxVolume); + } + updateTimer = Script.setInterval(this.update, UPDATE_INTERVAL_MSECS); + }; + + this.update = function() { + // Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar + var HYSTERESIS_FRACTION = 0.1; + var props = Entities.getEntityProperties(entity, [ "position" ]); + center = props.position; + var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center)); + if (distance <= range) { + var volume = (1.0 - distance / range) * maxVolume; + if (!soundPlaying && ambientSound.downloaded) { + soundPlaying = Audio.playSound(ambientSound, { loop: true, volume: volume }); + debugPrint("Starting ambient sound, volume: " + volume); + Entities.editEntity(entity, { color: { red: 255, green: 0, blue: 0 }}); + } else if (soundPlaying && soundPlaying.playing) { + soundPlaying.setOptions( { volume: volume } ); + } + } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { + soundPlaying.stop(); + soundPlaying = false; + Entities.editEntity(entity, { color: { red: 128, green: 128, blue: 128 }}); + debugPrint("Out of range, stopping ambient sound"); + } + } + + this.unload = function(entityID) { + debugPrint("Ambient sound unload"); + if (updateTimer) { + Script.clearInterval(updateTimer); + } + if (soundPlaying && soundPlaying.playing) { + soundPlaying.stop(); + } + }; + +}) From 36792e005a88ac0898f7b6936f65f0bf9b7d531c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 19 Dec 2016 14:19:39 -0800 Subject: [PATCH 014/118] initial commit --- domain-server/src/DomainServerSettingsManager.cpp | 7 ++++++- libraries/networking/src/NodeList.cpp | 6 ++++-- libraries/networking/src/NodeList.h | 2 +- libraries/script-engine/src/UsersScriptingInterface.h | 6 +++--- scripts/system/pal.js | 4 ++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 05cec07f80..c8895839ff 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -800,7 +800,12 @@ void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPoin usernameFromIDReplyPacket->write(nodeUUID.toRfc4122()); usernameFromIDReplyPacket->writeString(verifiedUsername); - qDebug() << "Sending username" << verifiedUsername << "associated with node" << nodeUUID; + // now put in the machine fingerprint + DomainServerNodeData* nodeData = reinterpret_cast(matchingNode->getLinkedData()); + QUuid machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid(); + usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122()); + + qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID; // Ship it! limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f76189b13a..98798ba1b5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -923,8 +923,10 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); // read the username from the packet QString username = message->readString(); + // read the machine fingerprint from the packet + QString machineFingerprintString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); - qDebug() << "Got username" << username << "for node" << nodeUUIDString; + qDebug() << "Got username" << username << "and machine fingerprint" << machineFingerprintString << "for node" << nodeUUIDString; - emit usernameFromIDReply(nodeUUIDString, username); + emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString); } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index d10c790f45..d3f04cedd8 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -111,7 +111,7 @@ signals: void receivedDomainServerList(); void ignoredNode(const QUuid& nodeID); void ignoreRadiusEnabledChanged(bool isIgnored); - void usernameFromIDReply(const QString& nodeID, const QString& username); + void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); private slots: void stopKeepalivePingTimer(); diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 1936a2e914..855dc06c11 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -101,10 +101,10 @@ signals: void enteredIgnoreRadius(); /**jsdoc - * Notifies scripts of the username associated with a UUID. - * @function Users.enteredIgnoreRadius + * Notifies scripts of the username and machine fingerprint associated with a UUID. + * @function Users.usernameFromIDReply */ - void usernameFromIDReply(const QString& nodeID, const QString& username); + void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); }; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 20f4167281..6df5531502 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -137,7 +137,7 @@ function populateUserList() { } // The function that handles the reply from the server -function usernameFromIDReply(id, username) { +function usernameFromIDReply(id, username, machineFingerprint) { var data; // If the ID we've received is our ID... if (AvatarList.getAvatar('').sessionUUID === id) { @@ -145,7 +145,7 @@ function usernameFromIDReply(id, username) { data = ['', username + ' (hidden)'] } else { // Set the data to contain the ID and the username+ID concat string. - data = [id, username + '/' + id]; + data = [id, username + '/' + machineFingerprint]; } print('Username Data:', JSON.stringify(data)); // Ship the data off to QML From a4d36008c3e5fa60883a14018b7722e42841112b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Dec 2016 14:50:28 -0800 Subject: [PATCH 015/118] remove offscreen surface log spam --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 4ab19744d4..e0c3317b55 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -609,7 +609,6 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function Date: Mon, 19 Dec 2016 15:12:48 -0800 Subject: [PATCH 016/118] Updates to tracing, removing log spam --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 14 +++++----- libraries/shared/src/StatTracker.cpp | 29 +++++++++++++-------- libraries/shared/src/StatTracker.h | 24 ++++++++++------- scripts/developer/tests/testTestMode.js | 25 ------------------ 4 files changed, 41 insertions(+), 51 deletions(-) delete mode 100644 scripts/developer/tests/testTestMode.js diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 4ab19744d4..3a803172d0 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -103,11 +103,14 @@ public: } void report() { - uint64_t now = usecTimestampNow(); - if ((now - _lastReport) > USECS_PER_SECOND * 5) { - _lastReport = now; - qCDebug(glLogging) << "Current offscreen texture count " << _allTextureCount; - qCDebug(glLogging) << "Current offscreen active texture count " << _activeTextureCount; + if (randFloat() < 0.01f) { + PROFILE_COUNTER(render_qml_gl, "offscreenTextures", { + { "total", QVariant::fromValue(_allTextureCount.load()) }, + { "active", QVariant::fromValue(_activeTextureCount.load()) }, + }); + PROFILE_COUNTER(render_qml_gl, "offscreenTextureMemory", { + { "value", QVariant::fromValue(_totalTextureUsage) } + }); } } @@ -189,7 +192,6 @@ private: std::unordered_map _textureSizes; Mutex _mutex; std::list _returnedTextures; - uint64_t _lastReport { 0 }; size_t _totalTextureUsage { 0 }; } offscreenTextures; diff --git a/libraries/shared/src/StatTracker.cpp b/libraries/shared/src/StatTracker.cpp index 4ec4e31797..0ac9ab0092 100644 --- a/libraries/shared/src/StatTracker.cpp +++ b/libraries/shared/src/StatTracker.cpp @@ -12,23 +12,30 @@ StatTracker::StatTracker() { } -QVariant StatTracker::getStat(QString name) { +QVariant StatTracker::getStat(const QString& name) { std::lock_guard lock(_statsLock); return _stats[name]; } -void StatTracker::editStat(QString name, EditStatFunction fn) { - std::lock_guard lock(_statsLock); - _stats[name] = fn(_stats[name]); +void StatTracker::setStat(const QString& name, int value) { + Lock lock(_statsLock); + _stats[name] = value; } -void StatTracker::incrementStat(QString name) { - std::lock_guard lock(_statsLock); - QVariant stat = _stats[name]; - _stats[name] = _stats[name].toInt() + 1; +void StatTracker::updateStat(const QString& name, int value) { + Lock lock(_statsLock); + auto itr = _stats.find(name); + if (_stats.end() == itr) { + _stats[name] = value; + } else { + *itr = *itr + value; + } } -void StatTracker::decrementStat(QString name) { - std::lock_guard lock(_statsLock); - _stats[name] = _stats[name].toInt() - 1; +void StatTracker::incrementStat(const QString& name) { + updateStat(name, 1); +} + +void StatTracker::decrementStat(const QString& name) { + updateStat(name, -1); } \ No newline at end of file diff --git a/libraries/shared/src/StatTracker.h b/libraries/shared/src/StatTracker.h index b500d9a821..38afc2c379 100644 --- a/libraries/shared/src/StatTracker.h +++ b/libraries/shared/src/StatTracker.h @@ -8,10 +8,13 @@ #pragma once -#include -#include -#include +#include +#include +#include +#include + #include + #include "DependencyManager.h" #include "Trace.h" @@ -20,13 +23,16 @@ using EditStatFunction = std::function; class StatTracker : public Dependency { public: StatTracker(); - QVariant getStat(QString name); - void editStat(QString name, EditStatFunction fn); - void incrementStat(QString name); - void decrementStat(QString name); + QVariant getStat(const QString& name); + void setStat(const QString& name, int value); + void updateStat(const QString& name, int mod); + void incrementStat(const QString& name); + void decrementStat(const QString& name); private: - std::mutex _statsLock; - QVariantMap _stats; + using Mutex = std::mutex; + using Lock = std::lock_guard; + Mutex _statsLock; + QHash _stats; }; class CounterStat { diff --git a/scripts/developer/tests/testTestMode.js b/scripts/developer/tests/testTestMode.js deleted file mode 100644 index b535f635e4..0000000000 --- a/scripts/developer/tests/testTestMode.js +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/12/12 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -Script.setTimeout(function() { - var loggingRules = "" + -"*.debug=false\n" + -"hifi.render.debug=true\n" + -"hifi.interface.debug=true\n" + -""; - Test.startTracing(loggingRules); -}, 1 * 1000); - - -Script.setTimeout(function() { - Test.stopTracing("h:/testScriptTrace.json.gz"); - Test.quit(); -}, 10 * 1000); - - From a45e4da75f84bef73d0bf2ea5c65ee9b7620b997 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 19 Dec 2016 15:22:42 -0800 Subject: [PATCH 017/118] Initialize AvatarMixerClientData with the proper node id (should not be zeros for everyone). Then, in interface, check identity packets for self and make sure they're handled. --- assignment-client/src/avatars/AvatarMixer.cpp | 8 ++++---- assignment-client/src/avatars/AvatarMixerClientData.h | 2 ++ libraries/avatars/src/AvatarHashMap.cpp | 7 +++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index ca7f931067..70950dc8fc 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -502,9 +502,9 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); if (avatar.processAvatarIdentity(identity)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); - nodeData->flagIdentityChange(); - nodeData->setReceivedIdentity(); - } + nodeData->flagIdentityChange(); + nodeData->setReceivedIdentity(); + } } } } @@ -600,7 +600,7 @@ void AvatarMixer::domainSettingsRequestComplete() { float domainMaximumScale = _domainMaximumScale; nodeList->linkedDataCreateCallback = [domainMinimumScale, domainMaximumScale] (Node* node) { - auto clientData = std::unique_ptr { new AvatarMixerClientData }; + auto clientData = std::unique_ptr { new AvatarMixerClientData(node->getUUID()) }; clientData->getAvatar().setDomainMinimumScale(domainMinimumScale); clientData->getAvatar().setDomainMaximumScale(domainMaximumScale); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 538da4aab0..16e886815f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -34,6 +34,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; class AvatarMixerClientData : public NodeData { Q_OBJECT public: + AvatarMixerClientData(const QUuid& nodeID = QUuid()) : NodeData(nodeID) {} + virtual ~AvatarMixerClientData() {} using HRCTime = p_high_resolution_clock::time_point; int parseData(ReceivedMessage& message) override; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index ea9a4f42f8..24c701aeda 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -133,6 +133,13 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // make sure this isn't for an ignored avatar auto nodeList = DependencyManager::get(); + static auto EMPTY = QUuid(); + if (identity.uuid == _avatarHash.value(EMPTY)->getSessionUUID()) { + // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an + // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), + // we make things match here. + identity.uuid = EMPTY; + } if (!nodeList->isIgnoringNode(identity.uuid)) { // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); From 5b9c19b39e98ee43b2c0bc224d79f8a24dd3fa5e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 19 Dec 2016 16:06:12 -0800 Subject: [PATCH 018/118] Versioning --- libraries/networking/src/udt/PacketHeaders.cpp | 3 +++ libraries/networking/src/udt/PacketHeaders.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index d3347f8e53..b0061317a8 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -80,6 +80,9 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AudioStreamStats: return static_cast(AudioVersion::SpaceBubbleChanges); + case PacketType::UsernameFromIDReply: + return static_cast(UsernameFromIDReplyVersion::HasMachineFingerprint); + default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 4a16e8ffe8..28b1b0417e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -241,4 +241,8 @@ enum class AudioVersion : PacketVersion { SpaceBubbleChanges, }; +enum class UsernameFromIDReplyVersion : PacketVersion { + HasMachineFingerprint = 18 +}; + #endif // hifi_PacketHeaders_h From df954cab1038507604be9fdbea5b22a21c4d7ca1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Dec 2016 16:15:10 -0800 Subject: [PATCH 019/118] remove some log spam, recatargorize script logs to allow fine grain filtering --- interface/src/ui/overlays/Circle3DOverlay.cpp | 3 --- interface/src/ui/overlays/Line3DOverlay.cpp | 3 --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 1 + libraries/script-engine/src/ScriptEngine.cpp | 4 +++- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index bea737baa9..ae0173f054 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -19,7 +19,6 @@ QString const Circle3DOverlay::TYPE = "circle3d"; Circle3DOverlay::Circle3DOverlay() { memset(&_minorTickMarksColor, 0, sizeof(_minorTickMarksColor)); memset(&_majorTickMarksColor, 0, sizeof(_majorTickMarksColor)); - qDebug() << "Building circle3d overlay"; } Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : @@ -40,7 +39,6 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) : _majorTicksVerticesID(GeometryCache::UNKNOWN_ID), _minorTicksVerticesID(GeometryCache::UNKNOWN_ID) { - qDebug() << "Building circle3d overlay"; } Circle3DOverlay::~Circle3DOverlay() { @@ -59,7 +57,6 @@ Circle3DOverlay::~Circle3DOverlay() { geometryCache->releaseID(_minorTicksVerticesID); } } - qDebug() << "Destroying circle3d overlay"; } void Circle3DOverlay::render(RenderArgs* args) { if (!_visible) { diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 795faa2748..97e7d825f2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -19,7 +19,6 @@ QString const Line3DOverlay::TYPE = "line3d"; Line3DOverlay::Line3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { - qDebug() << "Building line3D overlay"; } Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : @@ -28,11 +27,9 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : _end(line3DOverlay->_end), _geometryCacheID(DependencyManager::get()->allocateID()) { - qDebug() << "Building line3D overlay"; } Line3DOverlay::~Line3DOverlay() { - qDebug() << "Destryoing line3D overlay"; auto geometryCache = DependencyManager::get(); if (_geometryCacheID && geometryCache) { geometryCache->releaseID(_geometryCacheID); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index e0c3317b55..4ab19744d4 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -609,6 +609,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function(); +Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script") + static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ QString message = ""; for (int i = 0; i < context->argumentCount(); i++) { @@ -78,7 +80,7 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ } message += context->argument(i).toString(); } - qCDebug(scriptengine).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline + qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline message = message.replace("\\", "\\\\") .replace("\n", "\\n") From 4feec85337de18b98b763deb3b5404848710b397 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 19 Dec 2016 17:23:24 -0800 Subject: [PATCH 020/118] CR feedback Though this seems like a hard place to remember, when we make further changes. --- libraries/networking/src/udt/PacketHeaders.cpp | 5 +---- libraries/networking/src/udt/PacketHeaders.h | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index b0061317a8..071e1026f5 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -44,7 +44,7 @@ const QSet NON_SOURCED_PACKETS = QSet() PacketVersion versionForPacketType(PacketType packetType) { switch (packetType) { case PacketType::DomainList: - return static_cast(DomainListVersion::GetUsernameFromUUIDSupport); + return static_cast(DomainListVersion::GetMachineFingerprintFromUUIDSupport); case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: @@ -80,9 +80,6 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AudioStreamStats: return static_cast(AudioVersion::SpaceBubbleChanges); - case PacketType::UsernameFromIDReply: - return static_cast(UsernameFromIDReplyVersion::HasMachineFingerprint); - default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 28b1b0417e..d2f179b707 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -230,7 +230,8 @@ enum class DomainServerAddedNodeVersion : PacketVersion { enum class DomainListVersion : PacketVersion { PrePermissionsGrid = 18, PermissionsGrid, - GetUsernameFromUUIDSupport + GetUsernameFromUUIDSupport, + GetMachineFingerprintFromUUIDSupport }; enum class AudioVersion : PacketVersion { @@ -241,8 +242,4 @@ enum class AudioVersion : PacketVersion { SpaceBubbleChanges, }; -enum class UsernameFromIDReplyVersion : PacketVersion { - HasMachineFingerprint = 18 -}; - #endif // hifi_PacketHeaders_h From 0c9ff87feb6eb63a4925d8c098a45fccfe869b3a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Dec 2016 19:34:14 -0800 Subject: [PATCH 021/118] add support for an HMD script debug log window --- interface/src/Application.cpp | 3 +++ libraries/script-engine/src/ScriptEngines.cpp | 5 +++++ libraries/script-engine/src/ScriptEngines.h | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64934b47de..78cc1349dd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5269,6 +5269,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto scriptingInterface = DependencyManager::get(); scriptEngine->registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(scriptEngine); + + // connect this script engines printedMessage signal to the global ScriptEngines onPrintedMessage + connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get().data(), &ScriptEngines::onPrintedMessage); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index b3092be55a..9ddc8901db 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -32,6 +32,11 @@ ScriptsModel& getScriptsModel() { return scriptsModel; } +void ScriptEngines::onPrintedMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + emit printedMessage(message, scriptEngine->getFilename()); +} + ScriptEngines::ScriptEngines() : _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION) { diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index a9c273b2a7..06f94cd5f1 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -74,10 +74,15 @@ signals: void scriptCountChanged(); void scriptsReloading(); void scriptLoadError(const QString& filename, const QString& error); + void printedMessage(const QString& message, const QString& filename); + +public slots: + void onPrintedMessage(const QString& message); protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); + protected: friend class ScriptEngine; From 1e5e6a12db4a369c334fb06f90d243404c0c6369 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Dec 2016 19:34:24 -0800 Subject: [PATCH 022/118] add support for an HMD script debug log window --- scripts/developer/debugging/debugWindow.js | 25 +++++++++++++++ scripts/developer/debugging/debugWindow.qml | 35 +++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 scripts/developer/debugging/debugWindow.js create mode 100644 scripts/developer/debugging/debugWindow.qml diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js new file mode 100644 index 0000000000..c5d600458d --- /dev/null +++ b/scripts/developer/debugging/debugWindow.js @@ -0,0 +1,25 @@ +// +// debugWindow.js +// +// Brad Hefta-Gaub, created on 12/19/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 +// + + +// Set up the qml ui +var qml = Script.resolvePath('debugWindow.qml'); +var window = new OverlayWindow({ + title: 'Debug Window', + source: qml, + width: 400, height: 900, +}); +window.setPosition(25, 50); +window.closed.connect(function() { Script.stop(); }); + +// Demonstrate sending a message to the QML window +ScriptDiscoveryService.printedMessage.connect(function(message, scriptFileName) { + window.sendToQml("[" + scriptFileName + "] " + message); +}); diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml new file mode 100644 index 0000000000..867e5bca47 --- /dev/null +++ b/scripts/developer/debugging/debugWindow.qml @@ -0,0 +1,35 @@ +// +// InfoView.qml +// +// Created by Bradley Austin Davis on 27 Apr 2015 +// Copyright 2015 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 Hifi 1.0 as Hifi + + +Rectangle { + id: root + width: parent ? parent.width : 100 + height: parent ? parent.height : 100 + + property var channel; + + TextArea { + id: textArea + width: parent.width + height: parent.height + text:"" + } + + function fromScript(message) { + textArea.text += message + "\n"; + } +} + + From dbbed462b4284bbab0ea083d8b8f926d5c0e6146 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Dec 2016 22:24:25 -0800 Subject: [PATCH 023/118] cleanup naked qDebug() calls --- libraries/audio/src/AudioInjector.cpp | 8 +-- libraries/audio/src/AudioInjectorManager.cpp | 7 +- libraries/audio/src/InboundAudioStream.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 14 ++-- libraries/avatars/src/AvatarHashMap.cpp | 2 +- .../src/display-plugins/CompositorHelper.cpp | 1 - .../display-plugins/OpenGLDisplayPlugin.cpp | 2 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 1 - .../src/RenderableModelEntityItem.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 6 +- .../src/RenderableWebEntityItem.cpp | 10 +-- .../entities/src/AnimationPropertyGroup.cpp | 8 +-- .../entities/src/EntityActionInterface.cpp | 26 ++++---- .../entities/src/EntityEditPacketSender.cpp | 4 +- .../entities/src/EntityScriptingInterface.cpp | 12 ++-- libraries/entities/src/EntityTree.cpp | 12 ++-- libraries/entities/src/EntityTreeElement.cpp | 8 +-- libraries/entities/src/EntityTypes.h | 4 +- .../entities/src/KeyLightPropertyGroup.cpp | 12 ++-- libraries/entities/src/LineEntityItem.cpp | 6 +- libraries/entities/src/PolyLineEntityItem.cpp | 6 +- libraries/entities/src/PolyVoxEntityItem.cpp | 12 ++-- .../entities/src/SkyboxPropertyGroup.cpp | 6 +- libraries/entities/src/StagePropertyGroup.cpp | 16 ++--- libraries/fbx/src/FSTReader.cpp | 1 - .../gpu-gl/src/gpu/gl/GLBackendState.cpp | 4 +- .../gpu-gl/src/gpu/gl/GLTextureTransfer.cpp | 4 +- libraries/gpu/src/gpu/Buffer.h | 25 +++---- libraries/gpu/src/gpu/Texture.cpp | 8 +-- libraries/model/src/model/TextureMap.cpp | 2 +- libraries/networking/src/AccountManager.cpp | 4 +- .../networking/src/AssetResourceRequest.cpp | 2 +- libraries/networking/src/DomainHandler.cpp | 4 +- libraries/networking/src/FingerprintUtils.cpp | 19 +++--- .../networking/src/HTTPResourceRequest.cpp | 2 +- libraries/networking/src/NetworkPeer.cpp | 2 +- libraries/networking/src/NodeList.cpp | 14 ++-- libraries/networking/src/PacketReceiver.cpp | 2 - libraries/networking/src/ResourceManager.cpp | 6 +- libraries/networking/src/SandboxUtils.cpp | 15 +++-- .../networking/src/ThreadedAssignment.cpp | 8 ++- libraries/networking/src/udt/BasePacket.cpp | 4 +- libraries/networking/src/udt/Packet.cpp | 4 +- libraries/networking/src/udt/PacketList.cpp | 8 ++- libraries/networking/src/udt/SendQueue.cpp | 6 +- libraries/networking/src/udt/Socket.cpp | 2 +- libraries/octree/src/Logging.h | 16 +++++ libraries/octree/src/OctreeElement.cpp | 9 +-- libraries/physics/src/EntityMotionState.cpp | 4 +- libraries/physics/src/ObjectAction.cpp | 7 +- libraries/physics/src/ObjectActionOffset.cpp | 9 ++- libraries/physics/src/ObjectActionSpring.cpp | 8 ++- .../src/ObjectActionTravelOriented.cpp | 7 +- libraries/procedural/src/procedural/Logging.h | 16 +++++ .../procedural/src/procedural/Procedural.cpp | 10 ++- libraries/recording/src/recording/Clip.cpp | 3 +- .../src/recording/impl/BufferClip.cpp | 2 +- .../recording/src/recording/impl/FileClip.cpp | 2 +- .../src/recording/impl/PointerClip.cpp | 6 +- libraries/render-utils/src/FboCache.cpp | 3 +- libraries/render-utils/src/LightClusters.cpp | 3 +- libraries/render-utils/src/Model.cpp | 2 +- libraries/render/src/render/DrawTask.cpp | 5 +- libraries/render/src/render/Engine.cpp | 3 +- libraries/render/src/render/Scene.cpp | 4 +- libraries/render/src/render/ShapePipeline.cpp | 4 +- .../src/FileScriptingInterface.cpp | 29 ++++---- libraries/script-engine/src/ScriptCache.cpp | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 66 +++++++++---------- libraries/script-engine/src/ScriptsModel.cpp | 2 +- libraries/shared/src/CrashHelpers.h | 14 ++-- libraries/shared/src/Debug.h | 22 +++---- libraries/shared/src/HifiConfigVariantMap.cpp | 6 +- libraries/shared/src/PerfStat.cpp | 2 +- libraries/shared/src/PropertyFlags.h | 10 +-- libraries/shared/src/SettingHelpers.cpp | 7 +- libraries/shared/src/SharedUtil.cpp | 20 +++--- libraries/shared/src/SpatiallyNestable.cpp | 39 +++++------ libraries/shared/src/shared/FileLogger.cpp | 3 +- libraries/ui/src/FileDialogHelper.cpp | 3 +- libraries/ui/src/MainWindow.cpp | 3 +- libraries/ui/src/OffscreenUi.cpp | 7 +- libraries/ui/src/VrMenu.cpp | 3 +- plugins/hifiKinect/src/KinectPlugin.cpp | 3 - 84 files changed, 383 insertions(+), 316 deletions(-) create mode 100644 libraries/octree/src/Logging.h create mode 100644 libraries/procedural/src/procedural/Logging.h diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 3255f8c1eb..23e37d911e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -208,7 +208,7 @@ qint64 writeStringToStream(const QString& string, QDataStream& stream) { int64_t AudioInjector::injectNextFrame() { if (stateHas(AudioInjectorState::NetworkInjectionFinished)) { - qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; + qCDebug(audio) << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } @@ -231,7 +231,7 @@ int64_t AudioInjector::injectNextFrame() { auto numSamples = static_cast(_audioData.size() / sampleSize); auto targetSize = numSamples * sampleSize; if (targetSize != _audioData.size()) { - qDebug() << "Resizing audio that doesn't end at multiple of sample size, resizing from " + qCDebug(audio) << "Resizing audio that doesn't end at multiple of sample size, resizing from " << _audioData.size() << " to " << targetSize; _audioData.resize(targetSize); } @@ -297,7 +297,7 @@ int64_t AudioInjector::injectNextFrame() { } else { // no samples to inject, return immediately - qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; + qCDebug(audio) << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } @@ -388,7 +388,7 @@ int64_t AudioInjector::injectNextFrame() { if (currentFrameBasedOnElapsedTime - _nextFrame > MAX_ALLOWED_FRAMES_TO_FALL_BEHIND) { // If we are falling behind by more frames than our threshold, let's skip the frames ahead - qDebug() << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; + qCDebug(audio) << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; _nextFrame = currentFrameBasedOnElapsedTime; _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData.size(); } diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index b8560d1267..c66e209ea9 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -17,6 +17,7 @@ #include "AudioConstants.h" #include "AudioInjector.h" +#include "AudioLogging.h" AudioInjectorManager::~AudioInjectorManager() { _shouldStop = true; @@ -131,7 +132,7 @@ static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInje bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a lock. if (_injectors.size() >= MAX_INJECTORS_PER_THREAD) { - qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" + qCDebug(audio) << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" << MAX_INJECTORS_PER_THREAD << "current audio injectors."; return true; } @@ -140,7 +141,7 @@ bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a bool AudioInjectorManager::threadInjector(AudioInjector* injector) { if (_shouldStop) { - qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; return false; } @@ -169,7 +170,7 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { if (_shouldStop) { - qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; return false; } diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index ba60088117..36a6079546 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -153,7 +153,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); } else { - qDebug() << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; + qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; writeDroppableSilentFrames(networkFrames); // inform others of the mismatch auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f81a1169af..e2595bd55c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -147,14 +147,14 @@ void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { bool success; Transform trans = getTransform(success); if (!success) { - qDebug() << "Warning -- AvatarData::nextAttitude failed"; + qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; return; } trans.setTranslation(position); trans.setRotation(orientation); SpatiallyNestable::setTransform(trans, success); if (!success) { - qDebug() << "Warning -- AvatarData::nextAttitude failed"; + qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; } updateAttitude(); } @@ -390,7 +390,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { #ifdef WANT_DEBUG if (sendAll) { - qDebug() << "AvatarData::toByteArray" << cullSmallChanges << sendAll + qCDebug(avatars) << "AvatarData::toByteArray" << cullSmallChanges << sendAll << "rotations:" << rotationSentCount << "translations:" << translationSentCount << "largest:" << maxTranslationDimension << "size:" @@ -678,7 +678,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { #ifdef WANT_DEBUG if (numValidJointRotations > 15) { - qDebug() << "RECEIVING -- rotations:" << numValidJointRotations + qCDebug(avatars) << "RECEIVING -- rotations:" << numValidJointRotations << "translations:" << numValidJointTranslations << "size:" << (int)(sourceBuffer - startPosition); } @@ -1448,7 +1448,7 @@ QJsonObject AvatarData::toJson() const { bool success; Transform avatarTransform = getTransform(success); if (!success) { - qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform"; + qCWarning(avatars) << "Warning -- AvatarData::toJson couldn't get avatar transform"; } avatarTransform.setScale(getDomainLimitedScale()); if (recordingBasis) { @@ -1593,7 +1593,7 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { { QJsonObject obj = root; obj.remove(JSON_AVATAR_JOINT_ARRAY); - qDebug().noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented); + qCDebug(avatars).noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented); } #endif return QJsonDocument(root).toBinaryData(); @@ -1606,7 +1606,7 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { { QJsonObject obj = doc.object(); obj.remove(JSON_AVATAR_JOINT_ARRAY); - qDebug().noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented); + qCDebug(avatars).noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented); } #endif result.fromJson(doc.object()); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index ea9a4f42f8..571d665feb 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -160,7 +160,7 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { - qDebug() << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) + qCDebug(avatars) << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) << "from AvatarHashMap"; emit avatarRemovedEvent(removedAvatar->getSessionUUID()); } diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 56be8e1cf9..d8e0c59da7 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -152,7 +152,6 @@ CompositorHelper::CompositorHelper() : // auto cursor = Cursor::Manager::instance().getCursor(); // cursor->setIcon(Cursor::Icon::DEFAULT); // if (!_tooltipId.isEmpty()) { - // qDebug() << "Closing tooltip " << _tooltipId; // Tooltip::closeTip(_tooltipId); // _tooltipId.clear(); // } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 68c05062fd..c4846f6af8 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -96,7 +96,7 @@ public: Lock lock(_mutex); _shutdown = true; _condition.wait(lock, [&] { return !_shutdown; }); - qDebug() << "Present thread shutdown"; + qCDebug(displayPlugins) << "Present thread shutdown"; } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index e0c3dab1ed..a8b8ba3618 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -741,7 +741,6 @@ void HmdDisplayPlugin::compositeExtra() { } HmdDisplayPlugin::~HmdDisplayPlugin() { - qDebug() << "Destroying HmdDisplayPlugin"; } float HmdDisplayPlugin::stutterRate() const { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 0008d0fbcc..411433302d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -286,7 +286,7 @@ bool RenderableModelEntityItem::getAnimationFrame() { resizeJointArrays(); if (_jointMapping.size() != _model->getJointStateCount()) { - qDebug() << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" + qCDebug(entities) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" << _jointMapping.size() << _model->getJointStateCount(); assert(false); return false; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index e0c068ea6b..7646f0a454 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -890,7 +890,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { if (voxelXSize == 0 || voxelXSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION) { - qDebug() << "voxelSize is not reasonable, skipping decompressions." + qCDebug(entities) << "voxelSize is not reasonable, skipping decompressions." << voxelXSize << voxelYSize << voxelZSize << getName() << getID(); entity->setVoxelDataDirty(false); return; @@ -903,7 +903,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { QByteArray uncompressedData = qUncompress(compressedData); if (uncompressedData.size() != rawSize) { - qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" + qCDebug(entities) << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" << "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size() << getName() << getID(); entity->setVoxelDataDirty(false); @@ -973,7 +973,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { if (newVoxelData.size() > 1150) { // HACK -- until we have a way to allow for properties larger than MTU, don't update. // revert the active voxel-space to the last version that fit. - qDebug() << "compressed voxel data is too large" << entity->getName() << entity->getID(); + qCDebug(entities) << "compressed voxel data is too large" << entity->getName() << entity->getID(); return; } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 5290f3df19..7fad3b44d6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -46,7 +46,7 @@ EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID) : WebEntityItem(entityItemID) { - qDebug() << "Created web entity " << getID(); + qCDebug(entities) << "Created web entity " << getID(); _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); @@ -57,7 +57,7 @@ RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemI RenderableWebEntityItem::~RenderableWebEntityItem() { destroyWebSurface(); - qDebug() << "Destroyed web entity " << getID(); + qCDebug(entities) << "Destroyed web entity " << getID(); auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_geometryId); @@ -90,7 +90,7 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer } ++_currentWebCount; - qDebug() << "Building web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; + qCDebug(entities) << "Building web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; QSurface * currentSurface = currentContext->surface(); @@ -247,7 +247,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { void RenderableWebEntityItem::setSourceUrl(const QString& value) { if (_sourceUrl != value) { - qDebug() << "Setting web entity source URL to " << value; + qCDebug(entities) << "Setting web entity source URL to " << value; _sourceUrl = value; if (_webSurface) { AbstractViewStateInterface::instance()->postLambdaEvent([this] { @@ -358,7 +358,7 @@ void RenderableWebEntityItem::destroyWebSurface() { _hoverLeaveConnection = QMetaObject::Connection(); _webSurface.reset(); - qDebug() << "Delete web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; + qCDebug(entities) << "Delete web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl; } } diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 04fbd40d13..f6d08ad8b9 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -167,10 +167,10 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { void AnimationPropertyGroup::debugDump() const { - qDebug() << " AnimationPropertyGroup: ---------------------------------------------"; - qDebug() << " url:" << getURL() << " has changed:" << urlChanged(); - qDebug() << " fps:" << getFPS() << " has changed:" << fpsChanged(); - qDebug() << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); + qCDebug(entities) << " AnimationPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged(); + qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged(); + qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); } void AnimationPropertyGroup::listChangedProperties(QList& out) { diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index 2ce4ce5555..23e6fc0202 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -104,7 +104,7 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS return ACTION_TYPE_TRAVEL_ORIENTED; } - qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; + qCDebug(entities) << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; } @@ -129,7 +129,7 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return glm::vec3(0.0f); @@ -137,14 +137,14 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { - qDebug() << objectName << "argument" << argumentName << "must be a map"; + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map"; ok = false; return glm::vec3(0.0f); } QVariantMap resultVM = resultV.toMap(); if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { - qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z"; + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z"; ok = false; return glm::vec3(0.0f); } @@ -160,7 +160,7 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian float y = yV.toFloat(&yOk); float z = zV.toFloat(&zOk); if (!xOk || !yOk || !zOk) { - qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float."; + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float."; ok = false; return glm::vec3(0.0f); } @@ -178,7 +178,7 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return glm::quat(); @@ -186,14 +186,14 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { - qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); ok = false; return glm::quat(); } QVariantMap resultVM = resultV.toMap(); if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) { - qDebug() << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w"; + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w"; ok = false; return glm::quat(); } @@ -212,7 +212,7 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian float z = zV.toFloat(&zOk); float w = wV.toFloat(&wOk); if (!xOk || !yOk || !zOk || !wOk) { - qDebug() << objectName << "argument" << argumentName + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w of type float."; ok = false; return glm::quat(); @@ -231,7 +231,7 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return 0.0f; @@ -253,7 +253,7 @@ int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMa QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return 0.0f; @@ -275,7 +275,7 @@ QString EntityActionInterface::extractStringArgument(QString objectName, QVarian QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return ""; @@ -287,7 +287,7 @@ bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantM QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { if (required) { - qDebug() << objectName << "requires argument:" << argumentName; + qCDebug(entities) << objectName << "requires argument:" << argumentName; } ok = false; return false; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 4478e37003..e05db07d0d 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -52,12 +52,12 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, assert(_myAvatar); if (!entityTree) { - qDebug() << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; + qCDebug(entities) << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; return; } EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); if (!entity) { - qDebug() << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; + qCDebug(entities) << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; return; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 43947708ba..326ad96b66 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -183,7 +183,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } if (propertiesWithSimID.getParentID() == AVATAR_SELF_ID) { - qDebug() << "ERROR: Cannot set entity parent ID to the local-only MyAvatar ID"; + qCDebug(entities) << "ERROR: Cannot set entity parent ID to the local-only MyAvatar ID"; propertiesWithSimID.setParentID(QUuid()); } @@ -364,7 +364,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (!scriptSideProperties.parentIDChanged()) { properties.setParentID(entity->getParentID()); } else if (scriptSideProperties.getParentID() == AVATAR_SELF_ID) { - qDebug() << "ERROR: Cannot set entity parent ID to the local-only MyAvatar ID"; + qCDebug(entities) << "ERROR: Cannot set entity parent ID to the local-only MyAvatar ID"; properties.setParentID(QUuid()); } if (!scriptSideProperties.parentJointIndexChanged()) { @@ -925,12 +925,12 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, EntitySimulationPointer simulation = _entityTree->getSimulation(); entity = _entityTree->findEntityByEntityItemID(entityID); if (!entity) { - qDebug() << "actionWorker -- unknown entity" << entityID; + qCDebug(entities) << "actionWorker -- unknown entity" << entityID; return; } if (!simulation) { - qDebug() << "actionWorker -- no simulation" << entityID; + qCDebug(entities) << "actionWorker -- no simulation" << entityID; return; } @@ -1045,7 +1045,7 @@ EntityItemPointer EntityScriptingInterface::checkForTreeEntityAndTypeMatch(const EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (!entity) { - qDebug() << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID; + qCDebug(entities) << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID; return entity; } @@ -1305,7 +1305,7 @@ QVector EntityScriptingInterface::getChildrenIDs(const QUuid& parentID) { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(parentID); if (!entity) { - qDebug() << "EntityScriptingInterface::getChildrenIDs - no entity with ID" << parentID; + qCDebug(entities) << "EntityScriptingInterface::getChildrenIDs - no entity with ID" << parentID; return result; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7f4cfa78d0..4796dda671 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -343,7 +343,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti auto nodeList = DependencyManager::get(); if (!nodeList) { - qDebug() << "EntityTree::addEntity -- can't get NodeList"; + qCDebug(entities) << "EntityTree::addEntity -- can't get NodeList"; return nullptr; } @@ -1243,7 +1243,7 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { if (hasSomethingNewer) { int elapsed = usecTimestampNow() - considerEntitiesSince; int difference = considerEntitiesSince - sinceTime; - qDebug() << "EntityTree::hasEntitiesDeletedSince() sinceTime:" << sinceTime + qCDebug(entities) << "EntityTree::hasEntitiesDeletedSince() sinceTime:" << sinceTime << "considerEntitiesSince:" << considerEntitiesSince << "elapsed:" << elapsed << "difference:" << difference; } #endif @@ -1276,7 +1276,7 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { #ifdef EXTRA_ERASE_DEBUGGING - qDebug() << "EntityTree::processEraseMessage()"; + qCDebug(entities) << "EntityTree::processEraseMessage()"; #endif withWriteLock([&] { message.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME)); @@ -1296,7 +1296,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo QUuid entityID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); #ifdef EXTRA_ERASE_DEBUGGING - qDebug() << " ---- EntityTree::processEraseMessage() contained ID:" << entityID; + qCDebug(entities) << " ---- EntityTree::processEraseMessage() contained ID:" << entityID; #endif EntityItemID entityItemID(entityID); @@ -1318,7 +1318,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { #ifdef EXTRA_ERASE_DEBUGGING - qDebug() << "EntityTree::processEraseMessageDetails()"; + qCDebug(entities) << "EntityTree::processEraseMessageDetails()"; #endif const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); const unsigned char* dataAt = packetData; @@ -1347,7 +1347,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons processedBytes += encodedID.size(); #ifdef EXTRA_ERASE_DEBUGGING - qDebug() << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; + qCDebug(entities) << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; #endif EntityItemID entityItemID(entityID); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 4660d8780e..e374c6d289 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -329,7 +329,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData includeThisEntity = false; // too small, don't include it #ifdef WANT_LOD_DEBUGGING - qDebug() << "skipping entity - TOO SMALL - \n" + qCDebug(entities) << "skipping entity - TOO SMALL - \n" << "......id:" << entity->getID() << "\n" << "....name:" << entity->getName() << "\n" << "..bounds:" << entityBounds << "\n" @@ -341,7 +341,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData if (includeThisEntity) { #ifdef WANT_LOD_DEBUGGING - qDebug() << "including entity - \n" + qCDebug(entities) << "including entity - \n" << "......id:" << entity->getID() << "\n" << "....name:" << entity->getName() << "\n" << "....cell:" << getAACube(); @@ -472,7 +472,7 @@ bool EntityTreeElement::bestFitEntityBounds(EntityItemPointer entity) const { bool success; auto queryCube = entity->getQueryAACube(success); if (!success) { - qDebug() << "EntityTreeElement::bestFitEntityBounds couldn't get queryCube for" << entity->getName() << entity->getID(); + qCDebug(entities) << "EntityTreeElement::bestFitEntityBounds couldn't get queryCube for" << entity->getName() << entity->getID(); return false; } return bestFitBounds(queryCube); @@ -973,7 +973,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int } } else { #ifdef WANT_DEBUG - qDebug() << "Received packet for previously deleted entity [" << + qCDebug(entities) << "Received packet for previously deleted entity [" << entityItem->getID() << "] ignoring. (inside " << __FUNCTION__ << ")"; #endif } diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 016cda6cf7..fd09d4e67b 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -19,6 +19,8 @@ #include // for RenderArgs +#include "EntitiesLogging.h" + class EntityItem; using EntityItemPointer = std::shared_ptr; using EntityItemWeakPointer = std::weak_ptr; @@ -77,7 +79,7 @@ private: struct EntityRegistrationChecker { EntityRegistrationChecker(bool result, const char* debugMessage) { if (!result) { - qDebug() << debugMessage; + qCDebug(entities) << debugMessage; } } }; diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index 34ea7cd366..1011094266 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -60,12 +60,12 @@ void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { void KeyLightPropertyGroup::debugDump() const { - qDebug() << " KeyLightPropertyGroup: ---------------------------------------------"; - qDebug() << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2]; - qDebug() << " intensity:" << getIntensity(); - qDebug() << " direction:" << getDirection(); - qDebug() << " ambientIntensity:" << getAmbientIntensity(); - qDebug() << " ambientURL:" << getAmbientURL(); + qCDebug(entities) << " KeyLightPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2]; + qCDebug(entities) << " intensity:" << getIntensity(); + qCDebug(entities) << " direction:" << getDirection(); + qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity(); + qCDebug(entities) << " ambientURL:" << getAmbientURL(); } void KeyLightPropertyGroup::listChangedProperties(QList& out) { diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 01d713ad17..315de4938e 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -80,12 +80,12 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) { bool LineEntityItem::appendPoint(const glm::vec3& point) { if (_points.size() > MAX_POINTS_PER_LINE - 1) { - qDebug() << "MAX POINTS REACHED!"; + qCDebug(entities) << "MAX POINTS REACHED!"; return false; } glm::vec3 halfBox = getDimensions() * 0.5f; if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { - qDebug() << "Point is outside entity's bounding box"; + qCDebug(entities) << "Point is outside entity's bounding box"; return false; } _points << point; @@ -101,7 +101,7 @@ bool LineEntityItem::setLinePoints(const QVector& points) { for (int i = 0; i < points.size(); i++) { glm::vec3 point = points.at(i); if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { - qDebug() << "Point is outside entity's bounding box"; + qCDebug(entities) << "Point is outside entity's bounding box"; return false; } } diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index c9c5dd5df4..4cd59488c2 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -89,12 +89,12 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { if (_points.size() > MAX_POINTS_PER_LINE - 1) { - qDebug() << "MAX POINTS REACHED!"; + qCDebug(entities) << "MAX POINTS REACHED!"; return false; } glm::vec3 halfBox = getDimensions() * 0.5f; if ((point.x < -halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < -halfBox.z || point.z > halfBox.z)) { - qDebug() << "Point is outside entity's bounding box"; + qCDebug(entities) << "Point is outside entity's bounding box"; return false; } _points << point; @@ -142,7 +142,7 @@ bool PolyLineEntityItem::setLinePoints(const QVector& points) { if ((point.x < -halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < -halfBox.z || point.z > halfBox.z)) { - qDebug() << "Point is outside entity's bounding box"; + qCDebug(entities) << "Point is outside entity's bounding box"; return false; } } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 95d977c350..958b3ff88c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -71,29 +71,29 @@ void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { _voxelVolumeSize = glm::vec3(roundf(voxelVolumeSize.x), roundf(voxelVolumeSize.y), roundf(voxelVolumeSize.z)); if (_voxelVolumeSize.x < 1) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1"; _voxelVolumeSize.x = 1; } if (_voxelVolumeSize.x > MAX_VOXEL_DIMENSION) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to max"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to max"; _voxelVolumeSize.x = MAX_VOXEL_DIMENSION; } if (_voxelVolumeSize.y < 1) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to 1"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to 1"; _voxelVolumeSize.y = 1; } if (_voxelVolumeSize.y > MAX_VOXEL_DIMENSION) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to max"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to max"; _voxelVolumeSize.y = MAX_VOXEL_DIMENSION; } if (_voxelVolumeSize.z < 1) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to 1"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to 1"; _voxelVolumeSize.z = 1; } if (_voxelVolumeSize.z > MAX_VOXEL_DIMENSION) { - qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to max"; + qCDebug(entities) << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to max"; _voxelVolumeSize.z = MAX_VOXEL_DIMENSION; } }); diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index 51a99b0210..f8baf57856 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -34,9 +34,9 @@ void SkyboxPropertyGroup::merge(const SkyboxPropertyGroup& other) { void SkyboxPropertyGroup::debugDump() const { - qDebug() << " SkyboxPropertyGroup: ---------------------------------------------"; - qDebug() << " Color:" << getColor() << " has changed:" << colorChanged(); - qDebug() << " URL:" << getURL() << " has changed:" << urlChanged(); + qCDebug(entities) << " SkyboxPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " Color:" << getColor() << " has changed:" << colorChanged(); + qCDebug(entities) << " URL:" << getURL() << " has changed:" << urlChanged(); } void SkyboxPropertyGroup::listChangedProperties(QList& out) { diff --git a/libraries/entities/src/StagePropertyGroup.cpp b/libraries/entities/src/StagePropertyGroup.cpp index 9bd8f89307..cd486c96ac 100644 --- a/libraries/entities/src/StagePropertyGroup.cpp +++ b/libraries/entities/src/StagePropertyGroup.cpp @@ -67,14 +67,14 @@ void StagePropertyGroup::merge(const StagePropertyGroup& other) { void StagePropertyGroup::debugDump() const { - qDebug() << " StagePropertyGroup: ---------------------------------------------"; - qDebug() << " _sunModelEnabled:" << _sunModelEnabled; - qDebug() << " _latitude:" << _latitude; - qDebug() << " _longitude:" << _longitude; - qDebug() << " _altitude:" << _altitude; - qDebug() << " _day:" << _day; - qDebug() << " _hour:" << _hour; - qDebug() << " _automaticHourDay:" << _automaticHourDay; + qCDebug(entities) << " StagePropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _sunModelEnabled:" << _sunModelEnabled; + qCDebug(entities) << " _latitude:" << _latitude; + qCDebug(entities) << " _longitude:" << _longitude; + qCDebug(entities) << " _altitude:" << _altitude; + qCDebug(entities) << " _day:" << _day; + qCDebug(entities) << " _hour:" << _hour; + qCDebug(entities) << " _automaticHourDay:" << _automaticHourDay; } void StagePropertyGroup::listChangedProperties(QList& out) { diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 1f7999bdaa..3f0a629c8c 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -194,7 +194,6 @@ QVariantHash FSTReader::downloadMapping(const QString& url) { networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); - qDebug() << "Downloading avatar file at " << url; QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp index 1b40af6174..6f8c229184 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp @@ -11,6 +11,8 @@ #include "GLBackend.h" #include "GLState.h" +#include + using namespace gpu; using namespace gpu::gl; @@ -172,7 +174,7 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) { glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); } if (CHECK_GL_ERROR()) { - qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") + qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") << "Func=" << test.getFunction() << "Raw=" << test.getRaw(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp index 45403f4d4d..df9153d43e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTextureTransfer.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "GLShared.h" #include "GLTexture.h" @@ -155,7 +157,7 @@ bool GLTextureTransferHelper::process() { auto lastReportInterval = now - lastReport; if (lastReportInterval > USECS_PER_SECOND * 4) { lastReport = now; - qDebug() << "Texture list " << _transferringTextures.size(); + qCDebug(gpulogging) << "Texture list " << _transferringTextures.size(); } size_t transferCount = 0; diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 910b899d69..2507e8e0a6 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -13,6 +13,7 @@ #if _DEBUG #include +#include "GPULogging.h" #endif #include "Forward.h" @@ -318,13 +319,13 @@ public: template const T& get() const { #if _DEBUG if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; + qCDebug(gpulogging) << "Accessing null gpu::buffer!"; } if (sizeof(T) > (_buffer->getSize() - _offset)) { - qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); + qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); } if (sizeof(T) > _size) { - qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; + qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; } #endif const T* t = (reinterpret_cast (_buffer->getData() + _offset)); @@ -334,13 +335,13 @@ public: template T& edit() { #if _DEBUG if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; + qCDebug(gpulogging) << "Accessing null gpu::buffer!"; } if (sizeof(T) > (_buffer->getSize() - _offset)) { - qDebug() << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); + qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - _offset); } if (sizeof(T) > _size) { - qDebug() << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; + qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T) << " when bufferView size = " << _size; } #endif _buffer->markDirty(_offset, sizeof(T)); @@ -352,13 +353,13 @@ public: Resource::Size elementOffset = index * _stride + _offset; #if _DEBUG if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; + qCDebug(gpulogging) << "Accessing null gpu::buffer!"; } if (sizeof(T) > (_buffer->getSize() - elementOffset)) { - qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); + qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); } if (index > getNum()) { - qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); + qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); } #endif return *(reinterpret_cast (_buffer->getData() + elementOffset)); @@ -368,13 +369,13 @@ public: Resource::Size elementOffset = index * _stride + _offset; #if _DEBUG if (!_buffer) { - qDebug() << "Accessing null gpu::buffer!"; + qCDebug(gpulogging) << "Accessing null gpu::buffer!"; } if (sizeof(T) > (_buffer->getSize() - elementOffset)) { - qDebug() << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); + qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index << ", element size = " << sizeof(T) << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset); } if (index > getNum()) { - qDebug() << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); + qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index << " number elements = " << getNum(); } #endif _buffer->markDirty(elementOffset, sizeof(T)); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 19147ca98d..5b0c4c876a 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -42,7 +42,7 @@ std::atomic Texture::_enableSparseTextures { recommendedSparseTextures }; struct ReportTextureState { ReportTextureState() { - qDebug() << "[TEXTURE TRANSFER SUPPORT]" + qCDebug(gpulogging) << "[TEXTURE TRANSFER SUPPORT]" << "\n\tidealThreadCount:" << QThread::idealThreadCount() << "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures; } @@ -50,10 +50,10 @@ struct ReportTextureState { void Texture::setEnableSparseTextures(bool enabled) { #ifdef Q_OS_WIN - qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled; + qCDebug(gpulogging) << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Sparse Textures and Dynamic Texture Management:" << enabled; _enableSparseTextures = enabled; #else - qDebug() << "[TEXTURE TRANSFER SUPPORT] Sparse Textures and Dynamic Texture Management not supported on this platform."; + qCDebug(gpulogging) << "[TEXTURE TRANSFER SUPPORT] Sparse Textures and Dynamic Texture Management not supported on this platform."; #endif } @@ -114,7 +114,7 @@ Texture::Size Texture::getAllowedGPUMemoryUsage() { } void Texture::setAllowedGPUMemoryUsage(Size size) { - qDebug() << "New MAX texture memory " << BYTES_TO_MB(size) << " MB"; + qCDebug(gpulogging) << "New MAX texture memory " << BYTES_TO_MB(size) << " MB"; _allowedCPUMemoryUsage = size; } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3957d6c708..ab96a04f31 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -72,7 +72,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { } if (targetSize != srcImageSize) { - qDebug() << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y; + qDebug(modelLog) << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y; return srcImage.scaled(fromGlm(targetSize)); } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 1c3850f8cb..83f1504f00 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -633,7 +633,7 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI _isWaitingForKeypairResponse = true; // clear the current private key - qDebug() << "Clearing current private key in DataServerAccountInfo"; + qCDebug(networking) << "Clearing current private key in DataServerAccountInfo"; _accountInfo.setPrivateKey(QByteArray()); // setup a new QThread to generate the keypair on, in case it takes a while @@ -727,7 +727,7 @@ void AccountManager::processGeneratedKeypair() { } void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) { - qDebug() << "Uploaded public key to Metaverse API. RSA keypair generation is completed."; + qCDebug(networking) << "Uploaded public key to Metaverse API. RSA keypair generation is completed."; // public key upload complete - store the matching private key and persist the account to settings _accountInfo.setPrivateKey(_pendingPrivateKey); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 9a89465e02..540fb4767f 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -76,7 +76,7 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { switch (request->getError()) { case MappingRequest::NoError: // we have no error, we should have a resulting hash - use that to send of a request for that asset - qDebug() << "Got mapping for:" << path << "=>" << request->getHash(); + qCDebug(networking) << "Got mapping for:" << path << "=>" << request->getHash(); requestHash(request->getHash()); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 5d7340b2ce..c9106c7162 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -247,7 +247,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { } void DomainHandler::completedIceServerHostnameLookup() { - qDebug() << "ICE server socket is at" << _iceServerSockAddr; + qCDebug(networking) << "ICE server socket is at" << _iceServerSockAddr; DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerSocket); @@ -335,7 +335,7 @@ void DomainHandler::processDTLSRequirementPacket(QSharedPointer void DomainHandler::processICEResponsePacket(QSharedPointer message) { if (_icePeer.hasSockets()) { - qDebug() << "Received an ICE peer packet for domain-server but we already have sockets. Not processing."; + qCDebug(networking) << "Received an ICE peer packet for domain-server but we already have sockets. Not processing."; // bail on processing this packet if our ice peer already has sockets return; } diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 70a2f8ab2c..1990d356b6 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -10,6 +10,7 @@ // #include "FingerprintUtils.h" +#include "NetworkLogging.h" #include @@ -42,7 +43,7 @@ QString FingerprintUtils::getMachineFingerprintString() { IOObjectRelease(ioRegistryRoot); uuidString = QString::fromCFString(uuidCf); CFRelease(uuidCf); - qDebug() << "Mac serial number: " << uuidString; + qCDebug(networking) << "Mac serial number: " << uuidString; #endif //Q_OS_MAC #ifdef Q_OS_WIN @@ -53,7 +54,7 @@ QString FingerprintUtils::getMachineFingerprintString() { // users of this lib don't necessarily do so. hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { - qDebug() << "Failed to initialize COM library!"; + qCDebug(networking) << "Failed to initialize COM library!"; return uuidString; } @@ -65,7 +66,7 @@ QString FingerprintUtils::getMachineFingerprintString() { IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres)) { - qDebug() << "Failed to initialize WbemLocator"; + qCDebug(networking) << "Failed to initialize WbemLocator"; return uuidString; } @@ -88,7 +89,7 @@ QString FingerprintUtils::getMachineFingerprintString() { if (FAILED(hres)) { pLoc->Release(); - qDebug() << "Failed to connect to WMI"; + qCDebug(networking) << "Failed to connect to WMI"; return uuidString; } @@ -107,7 +108,7 @@ QString FingerprintUtils::getMachineFingerprintString() { if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); - qDebug() << "Failed to set security on proxy blanket"; + qCDebug(networking) << "Failed to set security on proxy blanket"; return uuidString; } @@ -123,7 +124,7 @@ QString FingerprintUtils::getMachineFingerprintString() { if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); - qDebug() << "query to get Win32_ComputerSystemProduct info"; + qCDebug(networking) << "query to get Win32_ComputerSystemProduct info"; return uuidString; } @@ -161,7 +162,7 @@ QString FingerprintUtils::getMachineFingerprintString() { pSvc->Release(); pLoc->Release(); - qDebug() << "Windows BIOS UUID: " << uuidString; + qCDebug(networking) << "Windows BIOS UUID: " << uuidString; #endif //Q_OS_WIN return uuidString; @@ -185,12 +186,12 @@ QUuid FingerprintUtils::getMachineFingerprint() { // read fallback key (if any) Settings settings; uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); - qDebug() << "read fallback maching fingerprint: " << uuid.toString(); + qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString(); if (uuid == QUuid()) { // no fallback yet, set one uuid = QUuid::createUuid(); settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); - qDebug() << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); } } return uuid; diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 64396ec62e..85da5de5b8 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -108,7 +108,7 @@ void HTTPResourceRequest::onRequestFinished() { case QNetworkReply::UnknownServerError: // Script.include('QUrl("https://httpbin.org/status/504")') case QNetworkReply::InternalServerError: // Script.include('QUrl("https://httpbin.org/status/500")') default: - qDebug() << "HTTPResourceRequest error:" << QMetaEnum::fromType().valueToKey(_reply->error()); + qCDebug(networking) << "HTTPResourceRequest error:" << QMetaEnum::fromType().valueToKey(_reply->error()); _result = Error; break; } diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index abbd262f3d..b52c54c468 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -158,7 +158,7 @@ void NetworkPeer::activateMatchingOrNewSymmetricSocket(const HifiSockAddr& match } void NetworkPeer::softReset() { - qDebug() << "Soft reset "; + qCDebug(networking) << "Soft reset "; // a soft reset should clear the sockets and reset the number of connection attempts _localSocket.clear(); _publicSocket.clear(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f76189b13a..4099ccc034 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -401,7 +401,7 @@ void NodeList::sendDomainServerCheckIn() { if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that - qDebug() << "Limit of silent domain checkins reached"; + qCDebug(networking) << "Limit of silent domain checkins reached"; emit limitOfSilentDomainCheckInsReached(); } @@ -629,7 +629,7 @@ void NodeList::processDomainServerAddedNode(QSharedPointer mess void NodeList::processDomainServerRemovedNode(QSharedPointer message) { // read the UUID from the packet, remove it if it exists QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - qDebug() << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID); + qCDebug(networking) << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID); killNodeWithUUID(nodeUUID); } @@ -793,7 +793,7 @@ void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) { // write the node ID to the packet ignorePacket->write(nodeID.toRfc4122()); - qDebug() << "Sending packet to ignore node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to ignore node" << uuidStringWithoutCurlyBraces(nodeID); // send off this ignore packet reliably to the matching node sendPacket(std::move(ignorePacket), *destinationNode); @@ -855,7 +855,7 @@ void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // write the node ID to the packet kickPacket->write(nodeID.toRfc4122()); - qDebug() << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to kick node" << uuidStringWithoutCurlyBraces(nodeID); sendPacket(std::move(kickPacket), _domainHandler.getSockAddr()); } else { @@ -880,7 +880,7 @@ void NodeList::muteNodeBySessionID(const QUuid& nodeID) { // write the node ID to the packet mutePacket->write(nodeID.toRfc4122()); - qDebug() << "Sending packet to mute node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to mute node" << uuidStringWithoutCurlyBraces(nodeID); sendPacket(std::move(mutePacket), *audioMixer); } else { @@ -909,7 +909,7 @@ void NodeList::requestUsernameFromSessionID(const QUuid& nodeID) { usernameFromIDRequestPacket->write(nodeID.toRfc4122()); } - qDebug() << "Sending packet to get username of node" << uuidStringWithoutCurlyBraces(nodeID); + qCDebug(networking) << "Sending packet to get username of node" << uuidStringWithoutCurlyBraces(nodeID); sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr()); } else { @@ -924,7 +924,7 @@ void NodeList::processUsernameFromIDReply(QSharedPointer messag // read the username from the packet QString username = message->readString(); - qDebug() << "Got username" << username << "for node" << nodeUUIDString; + qCDebug(networking) << "Got username" << username << "for node" << nodeUUIDString; emit usernameFromIDReply(nodeUUIDString, username); } diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 21ba7129d4..21db207375 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -324,8 +324,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer recei listenerIsDead = true; } } else { - // qDebug() << "Got verified unsourced packet list: " << QString(nlPacketList->getMessage()); - // one final check on the QPointer before we invoke if (listener.object) { success = listener.method.invoke(listener.object, diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index f33dbc161d..439d44c940 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -22,7 +22,7 @@ #include "FileResourceRequest.h" #include "HTTPResourceRequest.h" #include "NetworkAccessManager.h" - +#include "NetworkLogging.h" QThread ResourceManager::_thread; ResourceManager::PrefixMap ResourceManager::_prefixMap; @@ -51,7 +51,7 @@ QString ResourceManager::normalizeURL(const QString& urlString) { const auto& prefix = entry.first; const auto& replacement = entry.second; if (result.startsWith(prefix)) { - qDebug() << "Replacing " << prefix << " with " << replacement; + qCDebug(networking) << "Replacing " << prefix << " with " << replacement; result.replace(0, prefix.size(), replacement); } } @@ -105,7 +105,7 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q } else if (scheme == URL_SCHEME_ATP) { request = new AssetResourceRequest(normalizedURL); } else { - qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url(); + qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); return nullptr; } Q_ASSERT(request); diff --git a/libraries/networking/src/SandboxUtils.cpp b/libraries/networking/src/SandboxUtils.cpp index b16c169baa..bf17a0b1e4 100644 --- a/libraries/networking/src/SandboxUtils.cpp +++ b/libraries/networking/src/SandboxUtils.cpp @@ -27,6 +27,7 @@ #include "SandboxUtils.h" #include "NetworkAccessManager.h" +#include "NetworkLogging.h" void SandboxUtils::ifLocalSandboxRunningElse(std::function localSandboxRunningDoThis, @@ -64,10 +65,10 @@ void SandboxUtils::ifLocalSandboxRunningElse(std::function localSandboxR void SandboxUtils::runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName, bool noUpdater) { QString applicationDirPath = QFileInfo(QCoreApplication::applicationFilePath()).path(); QString serverPath = applicationDirPath + "/server-console/server-console.exe"; - qDebug() << "Application dir path is: " << applicationDirPath; - qDebug() << "Server path is: " << serverPath; - qDebug() << "autoShutdown: " << autoShutdown; - qDebug() << "noUpdater: " << noUpdater; + qCDebug(networking) << "Application dir path is: " << applicationDirPath; + qCDebug(networking) << "Server path is: " << serverPath; + qCDebug(networking) << "autoShutdown: " << autoShutdown; + qCDebug(networking) << "noUpdater: " << noUpdater; bool hasContentPath = !contentPath.isEmpty(); bool passArgs = autoShutdown || hasContentPath || noUpdater; @@ -92,9 +93,9 @@ void SandboxUtils::runLocalSandbox(QString contentPath, bool autoShutdown, QStri args << "--noUpdater"; } - qDebug() << applicationDirPath; - qDebug() << "Launching sandbox with:" << args; - qDebug() << QProcess::startDetached(serverPath, args); + qCDebug(networking) << applicationDirPath; + qCDebug(networking) << "Launching sandbox with:" << args; + qCDebug(networking) << QProcess::startDetached(serverPath, args); // Sleep a short amount of time to give the server a chance to start usleep(2000000); /// do we really need this?? diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 906bc240e6..e6e3c17db7 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -18,6 +18,8 @@ #include "ThreadedAssignment.h" +#include "NetworkLogging.h" + ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : Assignment(message), _isFinished(false), @@ -42,7 +44,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { if (_isFinished) { - qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up."; + qCDebug(networking) << "ThreadedAssignment::setFinished(true) called - finishing up."; auto nodeList = DependencyManager::get(); @@ -109,7 +111,7 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { // verify that the number of queued check-ins is not >= our max // the number of queued check-ins is cleared anytime we get a response from the domain-server if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - qDebug() << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server" + qCDebug(networking) << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server" << "Stopping the current assignment"; setFinished(true); } else { @@ -122,6 +124,6 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { } void ThreadedAssignment::domainSettingsRequestFailed() { - qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; + qCDebug(networking) << "Failed to retreive settings object from domain-server. Bailing on assignment."; setFinished(true); } diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 8a4b98de87..0540e60a0e 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -11,6 +11,8 @@ #include "BasePacket.h" +#include "../NetworkLogging.h" + using namespace udt; const qint64 BasePacket::PACKET_WRITE_ERROR = -1; @@ -131,7 +133,7 @@ void BasePacket::setPayloadSize(qint64 payloadSize) { Q_ASSERT(payloadSize <= _payloadCapacity); _payloadSize = payloadSize; } else { - qDebug() << "You can not call setPayloadSize for a non-writeable Packet."; + qCDebug(networking) << "You can not call setPayloadSize for a non-writeable Packet."; Q_ASSERT(false); } } diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index e852332317..f07ea63994 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -17,6 +17,8 @@ #include "Socket.h" +#include "../NetworkLogging.h" + using namespace udt; int packetMetaTypeId = qRegisterMetaType("Packet*"); @@ -106,7 +108,7 @@ Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& se } static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Unobfuscating packet .*"); - qDebug() << qPrintable(debugString); + qCDebug(networking) << qPrintable(debugString); #endif obfuscate(NoObfuscation); // Undo obfuscation diff --git a/libraries/networking/src/udt/PacketList.cpp b/libraries/networking/src/udt/PacketList.cpp index ea84548210..8651f9eed4 100644 --- a/libraries/networking/src/udt/PacketList.cpp +++ b/libraries/networking/src/udt/PacketList.cpp @@ -11,6 +11,8 @@ #include "PacketList.h" +#include "../NetworkLogging.h" + #include using namespace udt; @@ -102,7 +104,7 @@ std::unique_ptr PacketList::createPacketWithExtendedHeader() { if (!_extendedHeader.isEmpty()) { // add the extended header to the front of the packet if (packet->write(_extendedHeader) == -1) { - qDebug() << "Could not write extendedHeader in PacketList::createPacketWithExtendedHeader" + qCDebug(networking) << "Could not write extendedHeader in PacketList::createPacketWithExtendedHeader" << "- make sure that _extendedHeader is not larger than the payload capacity."; } } @@ -195,7 +197,7 @@ qint64 PacketList::writeData(const char* data, qint64 maxSize) { if (segmentSize + sizeRemaining > newPacket->getPayloadCapacity()) { // this is an unsupported case - the segment is bigger than the size of an individual packet // but the PacketList is not going to be sent ordered - qDebug() << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is" + qCDebug(networking) << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is" << "larger than the payload size."; Q_ASSERT(false); @@ -220,7 +222,7 @@ qint64 PacketList::writeData(const char* data, qint64 maxSize) { if (sizeRemaining > newPacket->getPayloadCapacity()) { // this is an unsupported case - attempting to write a block of data larger // than the capacity of a new packet in an unordered PacketList - qDebug() << "Error in PacketList::writeData - attempted to write data to an unordered packet that is" + qCDebug(networking) << "Error in PacketList::writeData - attempted to write data to an unordered packet that is" << "larger than the payload size."; Q_ASSERT(false); diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index c330f5516e..82f4084bb2 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -33,6 +33,8 @@ #include #include +#include "../NetworkLogging.h" + using namespace udt; using namespace std::chrono; @@ -299,12 +301,12 @@ void SendQueue::run() { // we've already been asked to stop before we even got a chance to start // don't start now #ifdef UDT_CONNECTION_DEBUG - qDebug() << "SendQueue asked to run after being told to stop. Will not run."; + qCDebug(networking) << "SendQueue asked to run after being told to stop. Will not run."; #endif return; } else if (_state == State::Running) { #ifdef UDT_CONNECTION_DEBUG - qDebug() << "SendQueue asked to run but is already running (according to state). Will not re-run."; + qCDebug(networking) << "SendQueue asked to run but is already running (according to state). Will not re-run."; #endif return; } diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index ae75e0fade..357f8a64d8 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -282,7 +282,7 @@ void Socket::clearConnections() { if (_connectionsHash.size() > 0) { // clear all of the current connections in the socket - qDebug() << "Clearing all remaining connections in Socket."; + qCDebug(networking) << "Clearing all remaining connections in Socket."; _connectionsHash.clear(); } } diff --git a/libraries/octree/src/Logging.h b/libraries/octree/src/Logging.h new file mode 100644 index 0000000000..72b5f40c43 --- /dev/null +++ b/libraries/octree/src/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Brad Hefta-Gaub on 2016-12-19 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_octree_Logging_h +#define hifi_octree_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(octree) + +#endif // hifi_octree_Logging_h diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index abcfd8fbaa..b56edcfcc6 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -23,6 +23,7 @@ #include #include "AACube.h" +#include "Logging.h" #include "OctalCode.h" #include "Octree.h" #include "OctreeConstants.h" @@ -444,18 +445,12 @@ void OctreeElement::printDebugDetails(const char* label) const { } } - QDebug elementDebug = qDebug().nospace(); - QString resultString; resultString.sprintf("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isDirty=%s shouldRender=%s\n children=", label, (double)_cube.getCorner().x, (double)_cube.getCorner().y, (double)_cube.getCorner().z, (double)_cube.getScale(), debug::valueOf(isLeaf()), debug::valueOf(isDirty()), debug::valueOf(getShouldRender())); - elementDebug << resultString; - - outputBits(childBits, &elementDebug); - qDebug("octalCode="); - printOctalCode(getOctalCode()); + qCDebug(octree).nospace() << resultString; } float OctreeElement::getEnclosingRadius() const { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 951728c4da..1833d0aba4 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -234,7 +234,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " "setPosition failed.*"); - qDebug() << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID(); + qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID(); } bool orientationSuccess; _entity->setOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false); @@ -242,7 +242,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " "setOrientation failed.*"); - qDebug() << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); + qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); } _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index ca64cabe5f..140de3a972 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -13,6 +13,9 @@ #include "ObjectAction.h" +#include "PhysicsLogging.h" + + ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) : btActionInterface(), EntityActionInterface(type, id), @@ -32,7 +35,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta }); if (!ownerEntity) { - qDebug() << "warning -- action with no entity removing self from btCollisionWorld."; + qCDebug(physics) << "warning -- action with no entity removing self from btCollisionWorld."; btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld); if (dynamicsWorld) { dynamicsWorld->removeAction(this); @@ -242,7 +245,7 @@ void ObjectAction::activateBody(bool forceActivation) { if (rigidBody) { rigidBody->activate(forceActivation); } else { - qDebug() << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody; + qCDebug(physics) << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody; } } diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 5c2999a0a1..f23b3985de 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -13,6 +13,9 @@ #include "ObjectActionOffset.h" +#include "PhysicsLogging.h" + + const uint16_t ObjectActionOffset::offsetVersion = 1; ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) : @@ -22,13 +25,13 @@ ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerE _linearTimeScale(FLT_MAX), _positionalTargetSet(false) { #if WANT_DEBUG - qDebug() << "ObjectActionOffset::ObjectActionOffset"; + qCDebug(physics) << "ObjectActionOffset::ObjectActionOffset"; #endif } ObjectActionOffset::~ObjectActionOffset() { #if WANT_DEBUG - qDebug() << "ObjectActionOffset::~ObjectActionOffset"; + qCDebug(physics) << "ObjectActionOffset::~ObjectActionOffset"; #endif } @@ -47,7 +50,7 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { - qDebug() << "ObjectActionOffset::updateActionWorker no rigidBody"; + qCDebug(physics) << "ObjectActionOffset::updateActionWorker no rigidBody"; return; } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index e67a681481..b22b3c3368 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -13,6 +13,8 @@ #include "ObjectActionSpring.h" +#include "PhysicsLogging.h" + const float SPRING_MAX_SPEED = 10.0f; const uint16_t ObjectActionSpring::springVersion = 1; @@ -29,13 +31,13 @@ ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerE _angularTimeScale(FLT_MAX), _rotationalTargetSet(true) { #if WANT_DEBUG - qDebug() << "ObjectActionSpring::ObjectActionSpring"; + qCDebug(physics) << "ObjectActionSpring::ObjectActionSpring"; #endif } ObjectActionSpring::~ObjectActionSpring() { #if WANT_DEBUG - qDebug() << "ObjectActionSpring::~ObjectActionSpring"; + qCDebug(physics) << "ObjectActionSpring::~ObjectActionSpring"; #endif } @@ -126,7 +128,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { - qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; + qCDebug(physics) << "ObjectActionSpring::updateActionWorker no rigidBody"; return; } diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp index 18d09d21d9..8f6d45c6f1 100644 --- a/libraries/physics/src/ObjectActionTravelOriented.cpp +++ b/libraries/physics/src/ObjectActionTravelOriented.cpp @@ -13,6 +13,7 @@ #include "QVariantGLM.h" #include "ObjectActionTravelOriented.h" +#include "PhysicsLogging.h" const uint16_t ObjectActionTravelOriented::actionVersion = 1; @@ -20,13 +21,13 @@ const uint16_t ObjectActionTravelOriented::actionVersion = 1; ObjectActionTravelOriented::ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity) : ObjectAction(ACTION_TYPE_TRAVEL_ORIENTED, id, ownerEntity) { #if WANT_DEBUG - qDebug() << "ObjectActionTravelOriented::ObjectActionTravelOriented"; + qCDebug(physics) << "ObjectActionTravelOriented::ObjectActionTravelOriented"; #endif } ObjectActionTravelOriented::~ObjectActionTravelOriented() { #if WANT_DEBUG - qDebug() << "ObjectActionTravelOriented::~ObjectActionTravelOriented"; + qCDebug(physics) << "ObjectActionTravelOriented::~ObjectActionTravelOriented"; #endif } @@ -43,7 +44,7 @@ void ObjectActionTravelOriented::updateActionWorker(btScalar deltaTimeStep) { ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { - qDebug() << "ObjectActionTravelOriented::updateActionWorker no rigidBody"; + qCDebug(physics) << "ObjectActionTravelOriented::updateActionWorker no rigidBody"; return; } const float MAX_TIMESCALE = 600.0f; // 10 min is a long time diff --git a/libraries/procedural/src/procedural/Logging.h b/libraries/procedural/src/procedural/Logging.h new file mode 100644 index 0000000000..3684d7c78f --- /dev/null +++ b/libraries/procedural/src/procedural/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Brad Hefta-Gaub on 2016-12-19 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_octree_Logging_h +#define hifi_octree_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(procedural) + +#endif // hifi_octree_Logging_h diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index a995ad5b2f..ac6163c227 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -21,6 +21,8 @@ #include "ProceduralCommon_frag.h" +#include "Logging.h" + // Userdata parsing constants static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; static const QString URL_KEY = "shaderUrl"; @@ -122,14 +124,16 @@ bool Procedural::parseShader(const QUrl& shaderPath) { if (_shaderUrl.isLocalFile()) { _shaderPath = _shaderUrl.toLocalFile(); #if WANT_DEBUG - qDebug() << "Shader path: " << _shaderPath; + qCDebug(procedural) << "Shader path: " << _shaderPath; #endif if (!QFile(_shaderPath).exists()) { _networkShader.reset(); return false;; } } else { - qDebug() << "Shader url: " << _shaderUrl; +#if WANT_DEBUG + qCDebug(procedural) << "Shader url: " << _shaderUrl; +#endif _networkShader = ShaderCache::instance().getShader(_shaderUrl); } @@ -271,7 +275,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm } // Leave this here for debugging - // qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str(); + // qCDebug(procedural) << "FragmentShader:\n" << fragmentShaderSource.c_str(); _fragmentShader = gpu::Shader::createPixel(fragmentShaderSource); _shader = gpu::Shader::createProgram(_vertexShader, _fragmentShader); diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index 1451724f23..27b6a2b6c9 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -9,6 +9,7 @@ #include "Clip.h" #include "Frame.h" +#include "Logging.h" #include "impl/FileClip.h" #include "impl/BufferClip.h" @@ -64,7 +65,7 @@ bool writeFrame(QIODevice& output, const Frame& frame, bool compressed = true) { if (written != sizeof(FrameType)) { return false; } - //qDebug() << "Writing frame with time offset " << frame.timeOffset; + //qDebug(recordingLog) << "Writing frame with time offset " << frame.timeOffset; written = output.write((char*)&(frame.timeOffset), sizeof(Frame::Time)); if (written != sizeof(Frame::Time)) { return false; diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index c40d9dd42a..fca4f3ce5e 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -33,7 +33,7 @@ void BufferClip::addFrame(FrameConstPointer newFrame) { ); auto newFrameIndex = itr - _frames.begin(); - //qDebug() << "Adding frame with time offset " << newFrame->timeOffset << " @ index " << newFrameIndex; + //qDebug(recordingLog) << "Adding frame with time offset " << newFrame->timeOffset << " @ index " << newFrameIndex; _frames.insert(_frames.begin() + newFrameIndex, Frame(*newFrame)); } diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index b153f5aa4a..7cfd7648fd 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -23,7 +23,7 @@ using namespace recording; FileClip::FileClip(const QString& fileName) : _file(fileName) { auto size = _file.size(); - qDebug() << "Opening file of size: " << size; + qDebug(recordingLog) << "Opening file of size: " << size; bool opened = _file.open(QIODevice::ReadOnly); if (!opened) { qCWarning(recordingLog) << "Unable to open file " << fileName; diff --git a/libraries/recording/src/recording/impl/PointerClip.cpp b/libraries/recording/src/recording/impl/PointerClip.cpp index 6f74391b4b..bd632a3f4a 100644 --- a/libraries/recording/src/recording/impl/PointerClip.cpp +++ b/libraries/recording/src/recording/impl/PointerClip.cpp @@ -32,7 +32,7 @@ FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { auto frameTypeObj = headerObj[Clip::FRAME_TYPE_MAP].toObject(); auto currentFrameTypes = Frame::getFrameTypes(); for (auto frameTypeName : frameTypeObj.keys()) { - qDebug() << frameTypeName; + qDebug(recordingLog) << frameTypeName; if (!currentFrameTypes.contains(frameTypeName)) { continue; } @@ -67,10 +67,10 @@ PointerFrameHeaderList parseFrameHeaders(uchar* const start, const size_t& size) current += header.size; results.push_back(header); } - qDebug() << "Parsed source data into " << results.size() << " frames"; + qDebug(recordingLog) << "Parsed source data into " << results.size() << " frames"; // int i = 0; // for (const auto& frameHeader : results) { -// qDebug() << "Frame " << i++ << " time " << frameHeader.timeOffset << " Type " << frameHeader.type; +// qDebug(recordingLog) << "Frame " << i++ << " time " << frameHeader.timeOffset << " Type " << frameHeader.type; // } return results; } diff --git a/libraries/render-utils/src/FboCache.cpp b/libraries/render-utils/src/FboCache.cpp index b6cca8116b..ff779f0b85 100644 --- a/libraries/render-utils/src/FboCache.cpp +++ b/libraries/render-utils/src/FboCache.cpp @@ -15,6 +15,7 @@ #include #include #include "ThreadHelpers.h" +#include "RenderUtilsLogging.h" FboCache::FboCache() { // Why do we even HAVE that lever? @@ -61,7 +62,7 @@ QOpenGLFramebufferObject* FboCache::getReadyFbo() { _destroyFboQueue.clear(); if (_readyFboQueue.empty()) { - qDebug() << "Building new offscreen FBO number " << _fboMap.size() + 1; + qCDebug(renderutils) << "Building new offscreen FBO number " << _fboMap.size() + 1; result = new QOpenGLFramebufferObject(_size, QOpenGLFramebufferObject::CombinedDepthStencil); _fboMap[result->texture()] = QSharedPointer(result); _readyFboQueue.push_back(result); diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index b9ba309fd5..d6c20f8f28 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -9,6 +9,7 @@ // #include "LightClusters.h" +#include "RenderUtilsLogging.h" #include @@ -327,7 +328,7 @@ uint32_t scanLightVolumeSphere(FrustumGrid& grid, const FrustumGrid::Planes plan clusterGrid[index].emplace_back(lightId); numClustersTouched++; } else { - qDebug() << "WARNING: LightClusters::scanLightVolumeSphere invalid index found ? numClusters = " << clusterGrid.size() << " index = " << index << " found from cluster xyz = " << x << " " << y << " " << z; + qCDebug(renderutils) << "WARNING: LightClusters::scanLightVolumeSphere invalid index found ? numClusters = " << clusterGrid.size() << " index = " << index << " found from cluster xyz = " << x << " " << y << " " << z; } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 03abfb35e4..bee7111150 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1293,7 +1293,7 @@ void Model::createVisibleRenderItemSet() { // all of our mesh vectors must match in size if ((int)meshes.size() != _meshStates.size()) { - qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + qCDebug(renderlogging) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 221251dae6..1c9a92c511 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -10,6 +10,7 @@ // #include "DrawTask.h" +#include "Logging.h" #include #include @@ -46,7 +47,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons } else if (key.hasOwnPipeline()) { item.render(args); } else { - qDebug() << "Item could not be rendered with invalid key" << key; + qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key; } } @@ -96,7 +97,7 @@ void render::renderStateSortShapes(const SceneContextPointer& sceneContext, cons } else if (key.hasOwnPipeline()) { ownPipelineBucket.push_back(item); } else { - qDebug() << "Item could not be rendered with invalid key" << key; + qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key; } } } diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index b955205a1a..3c9f2b643a 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -18,6 +18,7 @@ #include #include "EngineStats.h" +#include "Logging.h" using namespace render; @@ -44,7 +45,7 @@ void Engine::load() { QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &error); if (error.error == error.NoError) { config->setPresetList(doc.object()); - qDebug() << "Engine configuration file" << path << "loaded"; + qCDebug(renderlogging) << "Engine configuration file" << path << "loaded"; } else { qWarning() << "Engine configuration file" << path << "failed to load:" << error.errorString() << "at offset" << error.offset; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 9ff7120508..f918fc0bf6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -21,7 +21,7 @@ void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); } else { - qDebug() << "WARNING: PendingChanges::resetItem with a null payload!"; + qCDebug(renderlogging) << "WARNING: PendingChanges::resetItem with a null payload!"; removeItem(id); } } @@ -50,7 +50,7 @@ Scene::Scene(glm::vec3 origin, float size) : } Scene::~Scene() { - qDebug() << "Scene::~Scene()"; + qCDebug(renderlogging) << "Scene::~Scene()"; } ItemID Scene::allocateID() { diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 4bab517e30..48e8ee43d5 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -10,7 +10,7 @@ // #include "DependencyManager.h" - +#include "Logging.h" #include "ShapePipeline.h" #include @@ -107,7 +107,7 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke // The first time we can't find a pipeline, we should log it if (_missingKeys.find(key) == _missingKeys.end()) { _missingKeys.insert(key); - qDebug() << "Couldn't find a pipeline for" << key; + qCDebug(renderlogging) << "Couldn't find a pipeline for" << key; } return PipelinePointer(nullptr); } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index ad6a3cdf6f..14278837d3 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -22,9 +22,10 @@ #include #include #include -#include "ResourceManager.h" #include "FileScriptingInterface.h" +#include "ResourceManager.h" +#include "ScriptEngineLogging.h" FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent) { @@ -32,26 +33,26 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent } void FileScriptingInterface::runUnzip(QString path, QUrl url) { - qDebug() << "Url that was downloaded: " + url.toString(); - qDebug() << "Path where download is saved: " + path; + qCDebug(scriptengine) << "Url that was downloaded: " + url.toString(); + qCDebug(scriptengine) << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); QString tempDir = path; tempDir.remove(fileName); - qDebug() << "Temporary directory at: " + tempDir; + qCDebug(scriptengine) << "Temporary directory at: " + tempDir; if (!isTempDir(tempDir)) { - qDebug() << "Temporary directory mismatch; risk of losing files"; + qCDebug(scriptengine) << "Temporary directory mismatch; risk of losing files"; return; } QString file = unzipFile(path, tempDir); if (file != "") { - qDebug() << "Object file to upload: " + file; + qCDebug(scriptengine) << "Object file to upload: " + file; QUrl url = QUrl::fromLocalFile(file); emit unzipSuccess(url.toString()); } else { - qDebug() << "unzip failed"; + qCDebug(scriptengine) << "unzip failed"; } - qDebug() << "Removing temporary directory at: " + tempDir; + qCDebug(scriptengine) << "Removing temporary directory at: " + tempDir; QDir(tempDir).removeRecursively(); } @@ -94,7 +95,7 @@ QString FileScriptingInterface::convertUrlToPath(QUrl url) { QString newUrl; QString oldUrl = url.toString(); newUrl = oldUrl.section("filename=", 1, 1); - qDebug() << "Filename should be: " + newUrl; + qCDebug(scriptengine) << "Filename should be: " + newUrl; return newUrl; } @@ -116,12 +117,12 @@ QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { QString target = tempDir + "/model_repo"; QStringList list = JlCompress::extractDir(dirName, target); - qDebug() << list; + qCDebug(scriptengine) << list; if (!list.isEmpty()) { return list.front(); } else { - qDebug() << "Extraction failed"; + qCDebug(scriptengine) << "Extraction failed"; return ""; } @@ -130,13 +131,13 @@ QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { // this function is not in use void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { /*if (!file.isDir()) { - qDebug() << "Regular file logged: " + file.fileName(); + qCDebug(scriptengine) << "Regular file logged: " + file.fileName(); return; }*/ QFileInfoList files; if (file.fileName().contains(".zip")) { - qDebug() << "Extracting archive: " + file.fileName(); + qCDebug(scriptengine) << "Extracting archive: " + file.fileName(); JlCompress::extractDir(file.fileName()); } files = file.dir().entryInfoList(); @@ -146,7 +147,7 @@ void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) }*/ foreach (QFileInfo file, files) { - qDebug() << "Looking into file: " + file.fileName(); + qCDebug(scriptengine) << "Looking into file: " + file.fileName(); recursiveFileScan(file, dirName); } return; diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 9a2046beed..19534526c0 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -212,11 +212,11 @@ void ScriptCache::scriptContentAvailable() { if (!irrecoverable) { ++scriptRequest.numRetries; - qDebug() << "Script request failed: " << url; + qCDebug(scriptengine) << "Script request failed: " << url; int timeout = exp(scriptRequest.numRetries) * START_DELAY_BETWEEN_RETRIES; QTimer::singleShot(timeout, this, [this, url]() { - qDebug() << "Retrying script request: " << url; + qCDebug(scriptengine) << "Retrying script request: " << url; auto request = ResourceManager::createResourceRequest(nullptr, url); Q_ASSERT(request); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 45069b6391..ef0da5b248 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -551,7 +551,7 @@ void ScriptEngine::init() { void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif QMetaObject::invokeMethod(this, "registerValue", Q_ARG(const QString&, valueName), @@ -581,7 +581,7 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; #endif QMetaObject::invokeMethod(this, "registerGlobalObject", Q_ARG(const QString&, name), @@ -589,7 +589,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; + qCDebug(scriptengine) << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; #endif if (!globalObject().property(name).isValid()) { @@ -605,7 +605,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; #endif QMetaObject::invokeMethod(this, "registerFunction", Q_ARG(const QString&, name), @@ -614,7 +614,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name; + qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name; #endif QScriptValue scriptFun = newFunction(functionSignature, numArguments); @@ -624,7 +624,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name; #endif QMetaObject::invokeMethod(this, "registerFunction", Q_ARG(const QString&, name), @@ -633,7 +633,7 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name; + qCDebug(scriptengine) << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name; #endif QScriptValue object = globalObject().property(parent); @@ -647,7 +647,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func QScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " " name:" << name << "parent:" << parent; #endif QMetaObject::invokeMethod(this, "registerGetterSetter", @@ -658,7 +658,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; + qCDebug(scriptengine) << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; #endif QScriptValue setterFunction = newFunction(setter, 1); @@ -680,7 +680,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << " eventName:" << eventName; #endif QMetaObject::invokeMethod(this, "removeEventHandler", @@ -690,7 +690,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; + qCDebug(scriptengine) << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; #endif if (!_registeredHandlers.contains(entityID)) { @@ -710,7 +710,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << " eventName:" << eventName; #endif @@ -721,7 +721,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; + qCDebug(scriptengine) << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; #endif if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script... @@ -796,7 +796,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi if (QThread::currentThread() != thread()) { QScriptValue result; #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; #endif QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection, @@ -1010,7 +1010,7 @@ void ScriptEngine::stop(bool marshal) { void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; #endif QMetaObject::invokeMethod(this, "callAnimationStateHandler", Q_ARG(QScriptValue, callback), @@ -1182,7 +1182,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file))); QUrl defaultScriptsLoc = defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(thisURL)) { - qDebug() << "ScriptEngine::include -- skipping" << file << "-- outside of standard libraries"; + qCDebug(scriptengine) << "ScriptEngine::include -- skipping" << file << "-- outside of standard libraries"; continue; } isStandardLibrary = true; @@ -1297,7 +1297,7 @@ void ScriptEngine::load(const QString& loadFile) { // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHandlerArgs) { if (QThread::currentThread() != thread()) { - qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); return ; } @@ -1331,7 +1331,7 @@ void ScriptEngine::loadEntityScript(QWeakPointer theEngine, const QSharedPointer strongEngine = theEngine.toStrongRef(); if (strongEngine) { #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" + qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]"; #endif strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success); @@ -1344,7 +1344,7 @@ void ScriptEngine::loadEntityScript(QWeakPointer theEngine, const void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success; @@ -1360,7 +1360,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; + qCDebug(scriptengine) << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; #endif auto scriptCache = DependencyManager::get(); @@ -1448,7 +1448,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID; #endif @@ -1457,7 +1457,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] " "entityID:" << entityID; #endif @@ -1471,14 +1471,14 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { void ScriptEngine::unloadAllEntityScripts() { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif QMetaObject::invokeMethod(this, "unloadAllEntityScripts"); return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]"; + qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]"; #endif foreach(const EntityItemID& entityID, _entityScripts.keys()) { callEntityScriptMethod(entityID, "unload"); @@ -1486,13 +1486,13 @@ void ScriptEngine::unloadAllEntityScripts() { _entityScripts.clear(); #ifdef DEBUG_ENGINE_STATE - qDebug() << "---- CURRENT STATE OF ENGINE: --------------------------"; + qCDebug(scriptengine) << "---- CURRENT STATE OF ENGINE: --------------------------"; QScriptValueIterator it(globalObject()); while (it.hasNext()) { it.next(); - qDebug() << it.name() << ":" << it.value().toString(); + qCDebug(scriptengine) << it.name() << ":" << it.value().toString(); } - qDebug() << "--------------------------------------------------------"; + qCDebug(scriptengine) << "--------------------------------------------------------"; #endif // DEBUG_ENGINE_STATE } @@ -1563,7 +1563,7 @@ void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif @@ -1574,7 +1574,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName; #endif @@ -1595,7 +1595,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; #endif @@ -1606,7 +1606,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent"; #endif @@ -1627,7 +1627,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; #endif @@ -1639,7 +1639,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS return; } #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; #endif diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 8755195932..82ca26a03a 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -205,7 +205,7 @@ void ScriptsModel::downloadFinished() { qCDebug(scriptengine) << "Error: Received no data when loading default scripts"; } } else { - qDebug() << "Error: when loading default scripts --" << reply->error(); + qCDebug(scriptengine) << "Error: when loading default scripts --" << reply->error(); } reply->deleteLater(); diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h index 6233ab9177..1cc6749182 100644 --- a/libraries/shared/src/CrashHelpers.h +++ b/libraries/shared/src/CrashHelpers.h @@ -14,6 +14,8 @@ #ifndef hifi_CrashHelpers_h #define hifi_CrashHelpers_h +#include "SharedLogging.h" + namespace crash { class B; @@ -38,36 +40,36 @@ A::~A() { } void pureVirtualCall() { - qDebug() << "About to make a pure virtual call"; + qCDebug(shared) << "About to make a pure virtual call"; B b; } void doubleFree() { - qDebug() << "About to double delete memory"; + qCDebug(shared) << "About to double delete memory"; int* blah = new int(200); delete blah; delete blah; } void nullDeref() { - qDebug() << "About to dereference a null pointer"; + qCDebug(shared) << "About to dereference a null pointer"; int* p = nullptr; *p = 1; } void doAbort() { - qDebug() << "About to abort"; + qCDebug(shared) << "About to abort"; abort(); } void outOfBoundsVectorCrash() { - qDebug() << "std::vector out of bounds crash!"; + qCDebug(shared) << "std::vector out of bounds crash!"; std::vector v; v[0] = 42; } void newFault() { - qDebug() << "About to crash inside new fault"; + qCDebug(shared) << "About to crash inside new fault"; // Force crash with multiple large allocations while (true) { diff --git a/libraries/shared/src/Debug.h b/libraries/shared/src/Debug.h index 55d6d7975d..d817ccb69c 100644 --- a/libraries/shared/src/Debug.h +++ b/libraries/shared/src/Debug.h @@ -157,15 +157,15 @@ void Counter::log() { } } - qDebug() << "Counts for" << _name; + qCDebug(shared) << "Counts for" << _name; for (const auto& entry : results) { - qDebug() << entry.first << '\t' << entry.second << "entries"; + qCDebug(shared) << entry.first << '\t' << entry.second << "entries"; } if (_logLevel == LogLevel::SUMMARY) return; - qDebug() << "Entries"; + qCDebug(shared) << "Entries"; for (const auto& entry : _map) { - qDebug() << entry.first << '\t' << entry.second; + qCDebug(shared) << entry.first << '\t' << entry.second; } if (_logLevel == LogLevel::DETAILED) return; } @@ -208,7 +208,7 @@ void Tracker::set(const K& k, const size_t& v) { it->second = v; } else { // Unordered entry for k; dump log and fail - qDebug() << "Badly ordered entry detected:" << + qCDebug(shared) << "Badly ordered entry detected:" << k << _legend.at(it->second).c_str() << "->" << _legend.at(v).c_str(); log(); assert(false); @@ -226,9 +226,9 @@ void Tracker::log() { results.at(entry.second).push_back(entry.first); } - qDebug() << "Summary of" << _name; + qCDebug(shared) << "Summary of" << _name; for (auto i = 0; i < results.size(); ++i) { - qDebug() << _legend.at(i) << '\t' << results[i].size() << "entries"; + qCDebug(shared) << _legend.at(i) << '\t' << results[i].size() << "entries"; } if (_logLevel == LogLevel::SUMMARY) return; @@ -236,16 +236,16 @@ void Tracker::log() { for (const auto& entry : _duplicates) { size += entry.second.size(); } - qDebug() << "Duplicates" << size << "entries"; + qCDebug(shared) << "Duplicates" << size << "entries"; // TODO: Add more detail to duplicate logging if (_logLevel <= LogLevel::DUPLICATES) return; - qDebug() << "Entries"; + qCDebug(shared) << "Entries"; // Don't log the terminal case for (auto i = 0; i < _max; ++i) { - qDebug() << "----" << _legend.at(i) << "----"; + qCDebug(shared) << "----" << _legend.at(i) << "----"; for (const auto& entry : results[i]) { - qDebug() << "\t" << entry; + qCDebug(shared) << "\t" << entry; } } if (_logLevel <= LogLevel::DETAILED) return; diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index a1be6db448..5be6b2cd74 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -156,13 +156,13 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { auto dataDirectory = ServerPathUtils::getDataDirectory(); if (QDir().mkpath(dataDirectory)) { if (oldConfigFile.copy(_userConfigFilename)) { - qDebug() << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; + qCDebug(shared) << "Migrated config file from" << oldConfigFilename << "to" << _userConfigFilename; } else { - qWarning() << "Could not copy previous config file from" << oldConfigFilename << "to" << _userConfigFilename + qCWarning(shared) << "Could not copy previous config file from" << oldConfigFilename << "to" << _userConfigFilename << "- please try to copy manually and restart."; } } else { - qWarning() << "Could not create application data directory" << dataDirectory << "- unable to migrate previous config file."; + qCWarning(shared) << "Could not create application data directory" << dataDirectory << "- unable to migrate previous config file."; } } } diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index d202fdb248..13b3d44eda 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -129,7 +129,7 @@ void PerformanceTimer::setActive(bool active) { _records.clear(); } - qDebug() << "PerformanceTimer has been turned" << ((active) ? "on" : "off"); + qCDebug(shared) << "PerformanceTimer has been turned" << ((active) ? "on" : "off"); } } diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h index 833f06aa2b..28c0536593 100644 --- a/libraries/shared/src/PropertyFlags.h +++ b/libraries/shared/src/PropertyFlags.h @@ -26,7 +26,7 @@ #include #include "ByteCountCoding.h" -#include +#include "SharedLogging.h" templateclass PropertyFlags { public: @@ -255,14 +255,14 @@ template inline size_t PropertyFlags::decode(const QByteArr } template inline void PropertyFlags::debugDumpBits() { - qDebug() << "_minFlag=" << _minFlag; - qDebug() << "_maxFlag=" << _maxFlag; - qDebug() << "_trailingFlipped=" << _trailingFlipped; + qCDebug(shared) << "_minFlag=" << _minFlag; + qCDebug(shared) << "_maxFlag=" << _maxFlag; + qCDebug(shared) << "_trailingFlipped=" << _trailingFlipped; QString bits; for(int i = 0; i < _flags.size(); i++) { bits += (_flags.at(i) ? "1" : "0"); } - qDebug() << "bits:" << bits; + qCDebug(shared) << "bits:" << bits; } diff --git a/libraries/shared/src/SettingHelpers.cpp b/libraries/shared/src/SettingHelpers.cpp index f7d2510458..9e2d15fcd0 100644 --- a/libraries/shared/src/SettingHelpers.cpp +++ b/libraries/shared/src/SettingHelpers.cpp @@ -21,6 +21,7 @@ #include #include +#include "SharedLogging.h" QSettings::SettingsMap jsonDocumentToVariantMap(const QJsonDocument& document); QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map); @@ -32,7 +33,7 @@ bool readJSONFile(QIODevice& device, QSettings::SettingsMap& map) { auto document = QJsonDocument::fromJson(bytesRead, &jsonParseError); if (jsonParseError.error != QJsonParseError::NoError) { - qDebug() << "Error parsing QSettings file:" << jsonParseError.errorString(); + qCDebug(shared) << "Error parsing QSettings file:" << jsonParseError.errorString(); return false; } @@ -53,7 +54,7 @@ void loadOldINIFile(QSettings& settings) { QSettings iniSettings; if (!iniSettings.allKeys().isEmpty()) { - qDebug() << "No data in json settings file, trying to load old ini settings file."; + qCDebug(shared) << "No data in json settings file, trying to load old ini settings file."; for (auto key : iniSettings.allKeys()) { auto variant = iniSettings.value(key); @@ -75,7 +76,7 @@ void loadOldINIFile(QSettings& settings) { settings.setValue(key, variant); } - qDebug() << "Loaded" << settings.allKeys().size() << "keys from ini settings file."; + qCDebug(shared) << "Loaded" << settings.allKeys().size() << "keys from ini settings file."; } QSettings::setDefaultFormat(JSON_FORMAT); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 26483388e5..89116aa924 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -929,7 +929,7 @@ bool getProcessorInfo(ProcessorInfo& info) { GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); if (nullptr == glpi) { - qDebug() << "GetLogicalProcessorInformation is not supported."; + qCDebug(shared) << "GetLogicalProcessorInformation is not supported."; return false; } @@ -946,11 +946,11 @@ bool getProcessorInfo(ProcessorInfo& info) { returnLength); if (NULL == buffer) { - qDebug() << "Error: Allocation failure"; + qCDebug(shared) << "Error: Allocation failure"; return false; } } else { - qDebug() << "Error " << GetLastError(); + qCDebug(shared) << "Error " << GetLastError(); return false; } } else { @@ -992,19 +992,19 @@ bool getProcessorInfo(ProcessorInfo& info) { break; default: - qDebug() << "\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n"; + qCDebug(shared) << "\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n"; break; } byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ptr++; } - qDebug() << "GetLogicalProcessorInformation results:"; - qDebug() << "Number of NUMA nodes:" << numaNodeCount; - qDebug() << "Number of physical processor packages:" << processorPackageCount; - qDebug() << "Number of processor cores:" << processorCoreCount; - qDebug() << "Number of logical processors:" << logicalProcessorCount; - qDebug() << "Number of processor L1/L2/L3 caches:" + qCDebug(shared) << "GetLogicalProcessorInformation results:"; + qCDebug(shared) << "Number of NUMA nodes:" << numaNodeCount; + qCDebug(shared) << "Number of physical processor packages:" << processorPackageCount; + qCDebug(shared) << "Number of processor cores:" << processorCoreCount; + qCDebug(shared) << "Number of logical processors:" << logicalProcessorCount; + qCDebug(shared) << "Number of processor L1/L2/L3 caches:" << processorL1CacheCount << "/" << processorL2CacheCount << "/" << processorL3CacheCount; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index f75557f73f..cbe982b959 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -13,6 +13,7 @@ #include "DependencyManager.h" #include "SharedUtil.h" +#include "SharedLogging.h" #include "SpatiallyNestable.h" const float defaultAACubeSize = 1.0f; @@ -372,7 +373,7 @@ glm::vec3 SpatiallyNestable::getPosition() const { auto result = getPosition(success); #ifdef WANT_DEBUG if (!success) { - qDebug() << "Warning -- getPosition failed" << getID(); + qCDebug(shared) << "Warning -- getPosition failed" << getID(); } #endif return result; @@ -410,7 +411,7 @@ void SpatiallyNestable::setPosition(const glm::vec3& position) { setPosition(position, success); #ifdef WANT_DEBUG if (!success) { - qDebug() << "Warning -- setPosition failed" << getID(); + qCDebug(shared) << "Warning -- setPosition failed" << getID(); } #endif } @@ -424,7 +425,7 @@ glm::quat SpatiallyNestable::getOrientation() const { auto result = getOrientation(success); #ifdef WANT_DEBUG if (!success) { - qDebug() << "Warning -- getOrientation failed" << getID(); + qCDebug(shared) << "Warning -- getOrientation failed" << getID(); } #endif return result; @@ -462,7 +463,7 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation) { setOrientation(orientation, success); #ifdef WANT_DEBUG if (!success) { - qDebug() << "Warning -- setOrientation failed" << getID(); + qCDebug(shared) << "Warning -- setOrientation failed" << getID(); } #endif } @@ -488,7 +489,7 @@ glm::vec3 SpatiallyNestable::getVelocity() const { bool success; glm::vec3 result = getVelocity(success); if (!success) { - qDebug() << "Warning -- setVelocity failed" << getID(); + qCDebug(shared) << "Warning -- setVelocity failed" << getID(); } return result; } @@ -516,7 +517,7 @@ void SpatiallyNestable::setVelocity(const glm::vec3& velocity) { bool success; setVelocity(velocity, success); if (!success) { - qDebug() << "Warning -- setVelocity failed" << getID(); + qCDebug(shared) << "Warning -- setVelocity failed" << getID(); } } @@ -552,7 +553,7 @@ glm::vec3 SpatiallyNestable::getAngularVelocity() const { bool success; glm::vec3 result = getAngularVelocity(success); if (!success) { - qDebug() << "Warning -- getAngularVelocity failed" << getID(); + qCDebug(shared) << "Warning -- getAngularVelocity failed" << getID(); } return result; } @@ -569,7 +570,7 @@ void SpatiallyNestable::setAngularVelocity(const glm::vec3& angularVelocity) { bool success; setAngularVelocity(angularVelocity, success); if (!success) { - qDebug() << "Warning -- setAngularVelocity failed" << getID(); + qCDebug(shared) << "Warning -- setAngularVelocity failed" << getID(); } } @@ -599,7 +600,7 @@ const Transform SpatiallyNestable::getTransform() const { bool success; Transform result = getTransform(success); if (!success) { - qDebug() << "getTransform failed for" << getID(); + qCDebug(shared) << "getTransform failed for" << getID(); } return result; } @@ -612,7 +613,7 @@ const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, i if (depth > maxParentingChain) { success = false; // someone created a loop. break it... - qDebug() << "Parenting loop detected."; + qCDebug(shared) << "Parenting loop detected."; SpatiallyNestablePointer _this = getThisPointer(); _this->setParentID(QUuid()); bool setPositionSuccess; @@ -678,7 +679,7 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const { void SpatiallyNestable::setScale(const glm::vec3& scale) { // guard against introducing NaN into the transform if (isNaN(scale)) { - qDebug() << "SpatiallyNestable::setScale -- scale contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setScale -- scale contains NaN"; return; } @@ -698,7 +699,7 @@ void SpatiallyNestable::setScale(const glm::vec3& scale) { void SpatiallyNestable::setScale(float value) { // guard against introducing NaN into the transform if (value <= 0.0f) { - qDebug() << "SpatiallyNestable::setScale -- scale is zero or negative value"; + qCDebug(shared) << "SpatiallyNestable::setScale -- scale is zero or negative value"; return; } @@ -728,7 +729,7 @@ const Transform SpatiallyNestable::getLocalTransform() const { void SpatiallyNestable::setLocalTransform(const Transform& transform) { // guard against introducing NaN into the transform if (transform.containsNaN()) { - qDebug() << "SpatiallyNestable::setLocalTransform -- transform contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setLocalTransform -- transform contains NaN"; return; } @@ -756,7 +757,7 @@ glm::vec3 SpatiallyNestable::getLocalPosition() const { void SpatiallyNestable::setLocalPosition(const glm::vec3& position, bool tellPhysics) { // guard against introducing NaN into the transform if (isNaN(position)) { - qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setLocalPosition -- position contains NaN"; return; } bool changed = false; @@ -782,7 +783,7 @@ glm::quat SpatiallyNestable::getLocalOrientation() const { void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) { // guard against introducing NaN into the transform if (isNaN(orientation)) { - qDebug() << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN"; return; } bool changed = false; @@ -837,7 +838,7 @@ glm::vec3 SpatiallyNestable::getLocalScale() const { void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { // guard against introducing NaN into the transform if (isNaN(scale)) { - qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setLocalScale -- scale contains NaN"; return; } @@ -935,7 +936,7 @@ void SpatiallyNestable::checkAndAdjustQueryAACube() { void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { if (queryAACube.containsNaN()) { - qDebug() << "SpatiallyNestable::setQueryAACube -- cube contains NaN"; + qCDebug(shared) << "SpatiallyNestable::setQueryAACube -- cube contains NaN"; return; } _queryAACube = queryAACube; @@ -948,7 +949,7 @@ bool SpatiallyNestable::queryAABoxNeedsUpdate() const { bool success; AACube currentAACube = getMaximumAACube(success); if (!success) { - qDebug() << "can't getMaximumAACube for" << getID(); + qCDebug(shared) << "can't getMaximumAACube for" << getID(); return false; } @@ -1009,7 +1010,7 @@ AACube SpatiallyNestable::getQueryAACube() const { bool success; auto result = getQueryAACube(success); if (!success) { - qDebug() << "getQueryAACube failed for" << getID(); + qCDebug(shared) << "getQueryAACube failed for" << getID(); } return result; } diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp index 3f181b36d5..bea28b2b6f 100644 --- a/libraries/shared/src/shared/FileLogger.cpp +++ b/libraries/shared/src/shared/FileLogger.cpp @@ -20,6 +20,7 @@ #include "../NumericalConstants.h" #include "../SharedUtil.h" +#include "../SharedLogging.h" class FilePersistThread : public GenericQueueThread < QString > { Q_OBJECT @@ -88,7 +89,7 @@ void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfR if (file.copy(newFileName)) { file.open(QIODevice::WriteOnly | QIODevice::Truncate); file.close(); - qDebug() << "Rolled log file:" << newFileName; + qCDebug(shared) << "Rolled log file:" << newFileName; if (notifyListenersIfRolled) { emit rollingLogFile(newFileName); diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index 9d791ec562..2752de8592 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "FileDialogHelper.h" +#include "ui/Logging.h" #include #include @@ -47,7 +48,7 @@ QUrl FileDialogHelper::pathToUrl(const QString& path) { QUrl FileDialogHelper::saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters) { - qDebug() << "Calling save helper with " << saveText << " " << currentFolder << " " << selectionFilters; + qDebug(uiLogging) << "Calling save helper with " << saveText << " " << currentFolder << " " << selectionFilters; QFileInfo fileInfo(saveText); diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index 545d2dc902..c3ad59bf48 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -24,6 +24,7 @@ #include #include +#include "ui/Logging.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), @@ -35,7 +36,7 @@ MainWindow::MainWindow(QWidget* parent) : } MainWindow::~MainWindow() { - qDebug() << "Destroying main window"; + qCDebug(uiLogging) << "Destroying main window"; } void MainWindow::restoreGeometry() { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d9b15eebe0..60d80c6b35 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -23,6 +23,8 @@ #include "FileDialogHelper.h" #include "VrMenu.h" +#include "ui/Logging.h" + // Needs to match the constants in resources/qml/Global.js class OffscreenFlags : public QObject { @@ -73,7 +75,6 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { Q_ASSERT(event->type() == QEvent::ShortcutOverride); QObject* focusObject = getWindow()->focusObject(); if (focusObject != getWindow() && focusObject != getRootItem()) { - //qDebug() << "Swallowed shortcut " << static_cast(event)->key(); event->accept(); return true; } @@ -493,7 +494,7 @@ private: void OffscreenUi::createDesktop(const QUrl& url) { if (_desktop) { - qDebug() << "Desktop already created"; + qCDebug(uiLogging) << "Desktop already created"; return; } @@ -573,7 +574,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) { if (!result.isValid()) { return QString(); } - qDebug() << result.toString(); + qCDebug(uiLogging) << result.toString(); return result.toUrl().toLocalFile(); } diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 552d082aaa..73eb32ce17 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -15,6 +15,7 @@ #include #include "OffscreenUi.h" +#include "ui/Logging.h" static unsigned int USER_DATA_ID = 0; @@ -158,7 +159,7 @@ void VrMenu::addMenu(QMenu* menu) { void bindActionToQmlAction(QObject* qmlAction, QAction* action) { auto text = action->text(); if (text == "Login") { - qDebug() << "Login action " << action; + qDebug(uiLogging) << "Login action " << action; } new MenuUserData(action, qmlAction); diff --git a/plugins/hifiKinect/src/KinectPlugin.cpp b/plugins/hifiKinect/src/KinectPlugin.cpp index 844d4af962..2c837d83d3 100644 --- a/plugins/hifiKinect/src/KinectPlugin.cpp +++ b/plugins/hifiKinect/src/KinectPlugin.cpp @@ -286,12 +286,9 @@ bool KinectPlugin::initializeDefaultSensor() { } if (!_kinectSensor || FAILED(hr)) { - qDebug() << "No ready Kinect found!"; return false; } - qDebug() << "Kinect found WOOT!"; - return true; #else return false; From 6a7fa63327c1f67aa73554b5352bb7bc5a4bf3fe Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Dec 2016 09:00:01 -0800 Subject: [PATCH 024/118] classify ScriptEngine errors better, and send them as signal to JS --- interface/src/Application.cpp | 3 + libraries/script-engine/src/ScriptEngine.cpp | 98 +++++++++++-------- libraries/script-engine/src/ScriptEngine.h | 6 ++ libraries/script-engine/src/ScriptEngines.cpp | 20 ++++ libraries/script-engine/src/ScriptEngines.h | 10 +- scripts/developer/debugging/debugWindow.js | 12 +++ 6 files changed, 108 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 54589e1d9b..60bcf3a396 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5385,6 +5385,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri // connect this script engines printedMessage signal to the global ScriptEngines onPrintedMessage connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get().data(), &ScriptEngines::onPrintedMessage); + connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get().data(), &ScriptEngines::onErrorMessage); + connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get().data(), &ScriptEngines::onWarningMessage); + connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get().data(), &ScriptEngines::onInfoMessage); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ef0da5b248..6c3f6c7fa8 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -86,6 +86,8 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ .replace("\n", "\\n") .replace("\r", "\\r") .replace("'", "\\'"); + + // FIXME - this approach neeeds revisiting. print() comes here, which ends up doing an evaluate? engine->evaluate("Script.print('" + message + "')"); return QScriptValue(); @@ -130,20 +132,20 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID) return url + " [EntityID:" + entityID + "]"; } -static bool hasCorrectSyntax(const QScriptProgram& program) { +static bool hasCorrectSyntax(const QScriptProgram& program, ScriptEngine* reportingEngine) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); const auto line = QString::number(syntaxCheck.errorLineNumber()); const auto column = QString::number(syntaxCheck.errorColumnNumber()); const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); - qCWarning(scriptengine) << qPrintable(message); + reportingEngine->scriptErrorMessage(qPrintable(message)); return false; } return true; } -static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) { +static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName, ScriptEngine* reportingEngine) { if (engine.hasUncaughtException()) { const auto backtrace = engine.uncaughtExceptionBacktrace(); const auto exception = engine.uncaughtException().toString(); @@ -155,7 +157,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName static const auto lineSeparator = "\n "; message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); } - qCWarning(scriptengine) << qPrintable(message); + reportingEngine->scriptErrorMessage(qPrintable(message)); return true; } return false; @@ -170,20 +172,20 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam DependencyManager::get()->addScriptEngine(this); connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { - hadUncaughtExceptions(*this, _fileNameString); + hadUncaughtExceptions(*this, _fileNameString, this); }); setProcessEventsInterval(MSECS_PER_SECOND); } ScriptEngine::~ScriptEngine() { - qCDebug(scriptengine) << "Script Engine shutting down:" << getFilename(); + scriptInfoMessage("Script Engine shutting down:" + getFilename()); auto scriptEngines = DependencyManager::get(); if (scriptEngines) { scriptEngines->removeScriptEngine(this); } else { - qCWarning(scriptengine) << "Script destroyed after ScriptEngines!"; + scriptWarningMessage("Script destroyed after ScriptEngines!"); } } @@ -274,7 +276,7 @@ void ScriptEngine::runDebuggable() { } _lastUpdate = now; // Debug and clear exceptions - hadUncaughtExceptions(*this, _fileNameString); + hadUncaughtExceptions(*this, _fileNameString, this); }); timer->start(10); @@ -359,7 +361,7 @@ void ScriptEngine::waitTillDoneRunning() { QThread::yieldCurrentThread(); } - qCDebug(scriptengine) << "Script Engine has stopped:" << getFilename(); + scriptInfoMessage("Script Engine has stopped:" + getFilename()); } } @@ -398,10 +400,25 @@ void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scrip emit scriptLoaded(url.toString()); } +void ScriptEngine::scriptErrorMessage(const QString& message) { + qCCritical(scriptengine) << message; + emit errorMessage(message); +} + +void ScriptEngine::scriptWarningMessage(const QString& message) { + qCWarning(scriptengine) << message; + emit warningMessage(message); +} + +void ScriptEngine::scriptInfoMessage(const QString& message) { + qCInfo(scriptengine) << message; + emit infoMessage(message); +} + // FIXME - switch this to the new model of ScriptCache callbacks void ScriptEngine::errorInLoadingScript(const QUrl& url) { - qCDebug(scriptengine) << "ERROR Loading file:" << url.toString() << "line:" << __LINE__; - emit errorLoadingScript(_fileNameString); // ?? + scriptErrorMessage("ERROR Loading file:" + url.toString()); + emit errorLoadingScript(_fileNameString); } // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of @@ -809,7 +826,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // Check syntax const QScriptProgram program(sourceCode, fileName, lineNumber); - if (!hasCorrectSyntax(program)) { + if (!hasCorrectSyntax(program, this)) { return QScriptValue(); } @@ -817,7 +834,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName()); + const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName(), this); emit evaluationFinished(result, hadUncaughtException); return result; } @@ -934,10 +951,10 @@ void ScriptEngine::run() { _lastUpdate = now; // Debug and clear exceptions - hadUncaughtExceptions(*this, _fileNameString); + hadUncaughtExceptions(*this, _fileNameString, this); } - qCDebug(scriptengine) << "Script Engine stopping:" << getFilename(); + scriptInfoMessage("Script Engine stopping:" + getFilename()); stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); @@ -1044,7 +1061,7 @@ void ScriptEngine::timerFired() { { auto engine = DependencyManager::get(); if (!engine || engine->isStopped()) { - qCDebug(scriptengine) << "Script.timerFired() while shutting down is ignored... parent script:" << getFilename(); + scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename()); return; // bail early } } @@ -1084,7 +1101,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename(); + scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename()); return NULL; // bail early } @@ -1093,7 +1110,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename(); + scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename()); return NULL; // bail early } @@ -1153,8 +1170,8 @@ void ScriptEngine::print(const QString& message) { // all of the files have finished loading. void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.include() while shutting down is ignored..." - << "includeFiles:" << includeFiles << "parent script:" << getFilename(); + scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:" + + includeFiles.join(",") + "parent script:" + getFilename()); return; // bail early } QList urls; @@ -1182,7 +1199,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file))); QUrl defaultScriptsLoc = defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(thisURL)) { - qCDebug(scriptengine) << "ScriptEngine::include -- skipping" << file << "-- outside of standard libraries"; + scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries"); continue; } isStandardLibrary = true; @@ -1193,8 +1210,9 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && (currentSandboxURL.scheme() != "file" || !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) { - qCWarning(scriptengine) << "Script.include() ignoring file path" - << thisURL << "outside of original entity script" << currentSandboxURL; + + scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString() + + "outside of original entity script" + currentSandboxURL.toString()); } else { // We could also check here for CORS, but we don't yet. // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. @@ -1216,7 +1234,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac for (QUrl url : urls) { QString contents = data[url]; if (contents.isNull()) { - qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__; + scriptErrorMessage("Error loading file: " + url.toString()); } else { std::lock_guard lock(_lock); if (!_includedURLs.contains(url)) { @@ -1230,7 +1248,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); } else { - qCDebug(scriptengine) << "Script.include() skipping evaluation of previously included url:" << url; + scriptWarningMessage("Script.include() skipping evaluation of previously included url:" + url.toString()); } } } @@ -1259,8 +1277,8 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.include() while shutting down is ignored... " - << "includeFile:" << includeFile << "parent script:" << getFilename(); + scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:" + + includeFile + "parent script:" + getFilename()); return; // bail early } @@ -1274,13 +1292,13 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { // the Application or other context will connect to in order to know to actually load the script void ScriptEngine::load(const QString& loadFile) { if (DependencyManager::get()->isStopped()) { - qCDebug(scriptengine) << "Script.load() while shutting down is ignored... " - << "loadFile:" << loadFile << "parent script:" << getFilename(); + scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:" + + loadFile + "parent script:" + getFilename()); return; // bail early } if (!currentEntityIdentifier.isInvalidID()) { - qCWarning(scriptengine) << "Script.load() from entity script is ignored... " - << "loadFile:" << loadFile << "parent script:" << getFilename(); + scriptWarningMessage("Script.load() from entity script is ignored... loadFile:" + + loadFile + "parent script:" + getFilename()); return; // bail early } @@ -1368,7 +1386,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript"; QScriptProgram program(contents, fileName); - if (!hasCorrectSyntax(program)) { + if (!hasCorrectSyntax(program, this)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1396,7 +1414,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co }); testConstructor = sandbox.evaluate(program); } - if (hadUncaughtExceptions(sandbox, program.fileName())) { + if (hadUncaughtExceptions(sandbox, program.fileName(), this)) { return; } @@ -1410,10 +1428,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (testConstructorValue.size() > maxTestConstructorValueSize) { testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "..."; } - qCDebug(scriptengine) << "Error -- ScriptEngine::loadEntityScript() entity:" << entityID - << "failed to load entity script -- expected a function, got " + testConstructorType - << "," << testConstructorValue - << "," << scriptOrURL; + scriptErrorMessage("Error -- ScriptEngine::loadEntityScript() entity:" + entityID.toString() + + "failed to load entity script -- expected a function, got " + testConstructorType + + "," + testConstructorValue + + "," + scriptOrURL); if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); @@ -1513,7 +1531,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { QString filePath = QUrl(details.scriptText).toLocalFile(); auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch(); if (lastModified > details.lastModified) { - qCDebug(scriptengine) << "Reloading modified script " << details.scriptText; + scriptInfoMessage("Reloading modified script " + details.scriptText); QFile file(filePath); file.open(QIODevice::ReadOnly); @@ -1521,7 +1539,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { this->unloadEntityScript(entityID); this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true); if (!_entityScripts.contains(entityID)) { - qWarning() << "Reload script " << details.scriptText << " failed"; + scriptWarningMessage("Reload script " + details.scriptText + " failed"); } else { details = _entityScripts[entityID]; } @@ -1548,7 +1566,7 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s #else operation(); #endif - hadUncaughtExceptions(*this, _fileNameString); + hadUncaughtExceptions(*this, _fileNameString, this); currentEntityIdentifier = oldIdentifier; currentSandboxURL = oldSandboxURL; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 2b2cb3c81a..1606e70d28 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -174,6 +174,10 @@ public: void setEmitScriptUpdatesFunction(std::function func) { _emitScriptUpdates = func; } + void scriptErrorMessage(const QString& message); + void scriptWarningMessage(const QString& message); + void scriptInfoMessage(const QString& message); + public slots: void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); void updateMemoryCost(const qint64&); @@ -187,6 +191,8 @@ signals: void cleanupMenuItem(const QString& menuItemString); void printedMessage(const QString& message); void errorMessage(const QString& message); + void warningMessage(const QString& message); + void infoMessage(const QString& message); void runningStateChanged(); void evaluationFinished(QScriptValue result, bool isException); void loadScript(const QString& scriptName, bool isUserLoaded); diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 9ddc8901db..93c9823920 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -37,6 +37,26 @@ void ScriptEngines::onPrintedMessage(const QString& message) { emit printedMessage(message, scriptEngine->getFilename()); } +void ScriptEngines::onErrorMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + emit errorMessage(message, scriptEngine->getFilename()); +} + +void ScriptEngines::onWarningMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + emit warningMessage(message, scriptEngine->getFilename()); +} + +void ScriptEngines::onInfoMessage(const QString& message) { + auto scriptEngine = qobject_cast(sender()); + emit infoMessage(message, scriptEngine->getFilename()); +} + +void ScriptEngines::onErrorLoadingScript(const QString& url) { + auto scriptEngine = qobject_cast(sender()); + emit errorLoadingScript(url, scriptEngine->getFilename()); +} + ScriptEngines::ScriptEngines() : _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION) { diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 06f94cd5f1..583b6c0ea2 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -74,10 +74,18 @@ signals: void scriptCountChanged(); void scriptsReloading(); void scriptLoadError(const QString& filename, const QString& error); - void printedMessage(const QString& message, const QString& filename); + void printedMessage(const QString& message, const QString& engineName); + void errorMessage(const QString& message, const QString& engineName); + void warningMessage(const QString& message, const QString& engineName); + void infoMessage(const QString& message, const QString& engineName); + void errorLoadingScript(const QString& url, const QString& engineName); public slots: void onPrintedMessage(const QString& message); + void onErrorMessage(const QString& message); + void onWarningMessage(const QString& message); + void onInfoMessage(const QString& message); + void onErrorLoadingScript(const QString& url); protected slots: void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index c5d600458d..bff17afcd4 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -23,3 +23,15 @@ window.closed.connect(function() { Script.stop(); }); ScriptDiscoveryService.printedMessage.connect(function(message, scriptFileName) { window.sendToQml("[" + scriptFileName + "] " + message); }); + +ScriptDiscoveryService.warningMessage.connect(function(message, scriptFileName) { + window.sendToQml("[" + scriptFileName + "] WARNING - " + message); +}); + +ScriptDiscoveryService.errorMessage.connect(function(message, scriptFileName) { + window.sendToQml("[" + scriptFileName + "] ERROR - " + message); +}); + +ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) { + window.sendToQml("[" + scriptFileName + "] INFO - " + message); +}); From 2079b1ff7e6a70450935dedf91b74daf2058b492 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Dec 2016 10:45:36 -0800 Subject: [PATCH 025/118] cleanup comments --- interface/src/Application.cpp | 2 +- scripts/developer/debugging/debugWindow.js | 1 - scripts/developer/debugging/debugWindow.qml | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 60bcf3a396..354bf6e1d0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5383,7 +5383,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(scriptEngine); - // connect this script engines printedMessage signal to the global ScriptEngines onPrintedMessage + // connect this script engines printedMessage signal to the global ScriptEngines these various messages connect(scriptEngine, &ScriptEngine::printedMessage, DependencyManager::get().data(), &ScriptEngines::onPrintedMessage); connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get().data(), &ScriptEngines::onErrorMessage); connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get().data(), &ScriptEngines::onWarningMessage); diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index bff17afcd4..fb9f3f4847 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -19,7 +19,6 @@ var window = new OverlayWindow({ window.setPosition(25, 50); window.closed.connect(function() { Script.stop(); }); -// Demonstrate sending a message to the QML window ScriptDiscoveryService.printedMessage.connect(function(message, scriptFileName) { window.sendToQml("[" + scriptFileName + "] " + message); }); diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index 867e5bca47..fc332e3dcc 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -1,8 +1,8 @@ // -// InfoView.qml +// debugWindow.qml // -// Created by Bradley Austin Davis on 27 Apr 2015 -// Copyright 2015 High Fidelity, Inc. +// Brad Hefta-Gaub, created on 12/19/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 From f8bae603616b264c353d7f01500ba4421efb18ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Dec 2016 11:27:18 -0800 Subject: [PATCH 026/118] make debug log autoscroll when you add new content to it --- scripts/developer/debugging/debugWindow.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index fc332e3dcc..1e012ade6b 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -28,7 +28,7 @@ Rectangle { } function fromScript(message) { - textArea.text += message + "\n"; + textArea.append(message); } } From 03114fa6106e807a8e4732e9c0b07b3fcb4d3cdc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 11:42:37 -0800 Subject: [PATCH 027/118] add AvatarsPerSec trace stats --- interface/src/Application.cpp | 20 +++++++++++++++----- interface/src/avatar/Avatar.cpp | 20 +++++++++++++++++++- interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 8 +++++++- libraries/shared/src/Profile.cpp | 1 + libraries/shared/src/Profile.h | 1 + 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 691c5051b1..5ca2e1bc7f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -570,16 +570,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; const QStringList args = arguments(); - for (int i = 0; i < args.size() - 1; ++i) { + std::cout << "adebug args.size() = " << args.size() << std::endl; // adebug + for (int i = 0; i < args.size(); ++i) { + std::cout << i << " adebug '" << args.at(i).toStdString() << std::endl; // adebug if (args.at(i) == TEST_SCRIPT) { QString testScriptPath = args.at(i + 1); + std::cout << "adebug given test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); + std::cout << "adebug found test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug + ++i; + } else { + std::cout << "adebug did NOT find test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug + } + } else { + std::cout << "adebug test script not specified'" << std::endl; // adebug + if (args.at(i) == TRACE_FILE) { + QString traceFilePath = args.at(i + 1); + setProperty(hifi::properties::TRACING, traceFilePath); + DependencyManager::get()->startTracing(); } - } else if (args.at(i) == TRACE_FILE) { - QString traceFilePath = args.at(i + 1); - setProperty(hifi::properties::TRACING, traceFilePath); - DependencyManager::get()->startTracing(); } } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e83df85ff0..37840436fb 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -75,6 +75,19 @@ namespace render { } } +static uint64_t timeProcessingJoints = 0; +static int32_t numJointsProcessed = 0; + +float Avatar::getNumJointsProcessedPerSecond() { + float rate = 0.0f; + if (timeProcessingJoints > 0) { + rate = (float)(numJointsProcessed * USECS_PER_SECOND) / (float)timeProcessingJoints; + } + timeProcessingJoints = 0; + numJointsProcessed = 0; + return rate; +} + Avatar::Avatar(RigPointer rig) : AvatarData(), _skeletonOffset(0.0f), @@ -281,7 +294,7 @@ void Avatar::updateAvatarEntities() { void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - if (!isDead() && !_motionState) { + if (!_motionState && !isDead()) { DependencyManager::get()->addAvatarToSimulation(this); } animateScaleChanges(deltaTime); @@ -319,6 +332,7 @@ void Avatar::simulate(float deltaTime) { } } + uint64_t start = usecTimestampNow(); if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) { { PerformanceTimer perfTimer("skeleton"); @@ -345,6 +359,8 @@ void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("skeleton"); _skeletonModel->simulate(deltaTime, false); } + timeProcessingJoints += usecTimestampNow() - start; + numJointsProcessed += _jointData.size(); // update animation for display name fade in/out if ( _displayNameTargetAlpha != _displayNameAlpha) { @@ -627,6 +643,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { +#ifdef FOO // adebug _attachmentsToDelete.clear(); // check to see if when we added our models to the scene they were ready, if they were not ready, then @@ -650,6 +667,7 @@ void Avatar::fixupModelsInScene() { _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); +#endif // adebug } bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index dd0a9b2ab1..784f380028 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -57,6 +57,8 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: + static float getNumJointsProcessedPerSecond(); + explicit Avatar(RigPointer rig = nullptr); ~Avatar(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e2058ae1cf..fa695d9287 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -25,13 +25,13 @@ #endif +#include #include #include #include #include #include #include -#include #include "Application.h" #include "Avatar.h" @@ -129,6 +129,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { QReadLocker lock(&_hashLock); if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { + PROFILE_COUNTER(simulation_avatar, "AvatarsPerSec", { { "aps", 0.0f } }); + PROFILE_COUNTER(simulation_avatar, "JointsPerSec", { { "jps", 0.0f } }); return; } @@ -143,6 +145,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatars auto hashCopy = getHashCopy(); + uint64_t start = usecTimestampNow(); AvatarHash::iterator avatarIterator = hashCopy.begin(); while (avatarIterator != hashCopy.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); @@ -165,6 +168,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); + float avatarsPerSecond = (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start); + PROFILE_COUNTER(simulation_avatar, "AvatarsPerSec", { { "aps", avatarsPerSecond } }); + PROFILE_COUNTER(simulation_avatar, "JointsPerSec", { { "jps", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 4c5882348e..33c5c15862 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -18,6 +18,7 @@ Q_LOGGING_CATEGORY(trace_resource_network, "trace.resource.network") Q_LOGGING_CATEGORY(trace_resource_parse, "trace.resource.parse") Q_LOGGING_CATEGORY(trace_simulation, "trace.simulation") Q_LOGGING_CATEGORY(trace_simulation_animation, "trace.simulation.animation") +Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar") Q_LOGGING_CATEGORY(trace_simulation_physics, "trace.simulation.physics") #if defined(NSIGHT_FOUND) diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index 6d02a0939f..57a98deb73 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -22,6 +22,7 @@ Q_DECLARE_LOGGING_CATEGORY(trace_resource_parse) Q_DECLARE_LOGGING_CATEGORY(trace_resource_network) Q_DECLARE_LOGGING_CATEGORY(trace_simulation) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation) +Q_DECLARE_LOGGING_CATEGORY(trace_simulation_avatar) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics) class Duration { From e2dfbf1b023aaa0f568d9fc9fbf6f0c1f82439c1 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Dec 2016 12:02:13 -0800 Subject: [PATCH 028/118] Addressed PR feedback - now dynamically reads userdata --- .../tutorials/entity_scripts/ambientSound.js | 78 ++++++++++++------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index a387f00c10..19b082e157 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -3,13 +3,12 @@ // This entity script will allow you to create an ambient sound that loops when a person is within a given // range of this entity. Great way to add one or more ambisonic soundfields to your environment. // -// In the userData section for the entity, add two values: +// In the userData section for the entity, add/edit three values: // userData.SoundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. // userData.range should be an integer for the max distance away from the entity where the sound will be audible. -// userData.volume is the max volume at which the clip should play. +// userData.volume is the max volume at which the clip should play. Defaults to 1.0 full volume) // -// Note: When you update the above values, you need to reload the entity script for it to see the changes. Also, -// remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is +// Remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is // large enough to be loaded at the range you set, particularly for large ranges. // // Copyright 2016 High Fidelity, Inc. @@ -24,7 +23,7 @@ var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; var DEFAULT_VOLUME = 1.0; - var soundURL = DEFAULT_URL; + var soundURL = ""; var range = DEFAULT_RANGE; var maxVolume = DEFAULT_VOLUME; var UPDATE_INTERVAL_MSECS = 100; @@ -33,40 +32,61 @@ var ambientSound; var center; var soundPlaying = false; - var updateTimer = false; - - var WANT_DEBUG = false; + var checkTimer = false; + var _this; + var WANT_DEBUG = true; function debugPrint(string) { if (WANT_DEBUG) { print(string); } } + this.updateSettings = function() { + // Check user data on the entity for any changes + var oldSoundURL = soundURL; + var props = Entities.getEntityProperties(entity, [ "userData" ]); + if (props.userData) { + var data = JSON.parse(props.userData); + if (data.soundURL && !(soundURL === data.soundURL)) { + soundURL = data.soundURL; + debugPrint("Read ambient sound URL: " + soundURL); + } else if (!data.soundURL) { + soundURL = DEFAULT_URL; + } + if (data.range && !(range === data.range)) { + range = data.range; + debugPrint("Read ambient sound range: " + range); + } + if (data.volume && !(maxVolume === data.volume)) { + maxVolume = data.volume; + debugPrint("Read ambient sound volume: " + maxVolume); + } + } + if (!(soundURL === oldSoundURL) || (soundURL === "")) { + debugPrint("Loading ambient sound into cache"); + ambientSound = SoundCache.getSound(soundURL); + if (soundPlaying && soundPlaying.playing) { + soundPlaying.stop(); + soundPlaying = false; + Entities.editEntity(entity, { color: { red: 128, green: 128, blue: 128 }}); + debugPrint("Restarting ambient sound"); + } + } + } + this.preload = function(entityID) { // Load the sound and range from the entity userData fields, and note the position of the entity. debugPrint("Ambient sound preload"); entity = entityID; - var props = Entities.getEntityProperties(entityID, [ "userData" ]); - var data = JSON.parse(props.userData); - if (data.soundURL) { - soundURL = data.soundURL; - debugPrint("Read ambient sound URL: " + soundURL); - } - ambientSound = SoundCache.getSound(soundURL); - if (data.range) { - range = data.range; - debugPrint("Read ambient sound range: " + range); - } - if (data.volume) { - maxVolume = data.volume; - debugPrint("Read ambient sound volume: " + maxVolume); - } - updateTimer = Script.setInterval(this.update, UPDATE_INTERVAL_MSECS); + _this = this; + checkTimer = Script.setInterval(this.maybeUpdate, UPDATE_INTERVAL_MSECS); }; - this.update = function() { + this.maybeUpdate = function() { // Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar + _this.updateSettings(); + var props = Entities.getEntityProperties(entity); var HYSTERESIS_FRACTION = 0.1; var props = Entities.getEntityProperties(entity, [ "position" ]); center = props.position; @@ -74,7 +94,7 @@ if (distance <= range) { var volume = (1.0 - distance / range) * maxVolume; if (!soundPlaying && ambientSound.downloaded) { - soundPlaying = Audio.playSound(ambientSound, { loop: true, volume: volume }); + soundPlaying = Audio.playSound(ambientSound, { loop: true, localOnly: true, volume: volume }); debugPrint("Starting ambient sound, volume: " + volume); Entities.editEntity(entity, { color: { red: 255, green: 0, blue: 0 }}); } else if (soundPlaying && soundPlaying.playing) { @@ -90,12 +110,12 @@ this.unload = function(entityID) { debugPrint("Ambient sound unload"); - if (updateTimer) { - Script.clearInterval(updateTimer); + if (checkTimer) { + Script.clearInterval(checkTimer); } if (soundPlaying && soundPlaying.playing) { soundPlaying.stop(); } }; -}) +}) From 632af18c1d601662fb1d96184e71b379c8662644 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Dec 2016 12:09:42 -0800 Subject: [PATCH 029/118] PR feedback - flag for setting color --- scripts/tutorials/entity_scripts/ambientSound.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 19b082e157..b3ec919078 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -35,6 +35,10 @@ var checkTimer = false; var _this; + var WANT_COLOR_CHANGE = true; + var COLOR_OFF = { red: 128, green: 128, blue: 128 }; + var COLOR_ON = { red: 255, green: 0, blue: 0 }; + var WANT_DEBUG = true; function debugPrint(string) { if (WANT_DEBUG) { @@ -69,7 +73,9 @@ if (soundPlaying && soundPlaying.playing) { soundPlaying.stop(); soundPlaying = false; - Entities.editEntity(entity, { color: { red: 128, green: 128, blue: 128 }}); + if (WANT_COLOR_CHANGE) { + Entities.editEntity(entity, { color: COLOR_OFF }); + } debugPrint("Restarting ambient sound"); } } @@ -96,7 +102,10 @@ if (!soundPlaying && ambientSound.downloaded) { soundPlaying = Audio.playSound(ambientSound, { loop: true, localOnly: true, volume: volume }); debugPrint("Starting ambient sound, volume: " + volume); - Entities.editEntity(entity, { color: { red: 255, green: 0, blue: 0 }}); + if (WANT_COLOR_CHANGE) { + Entities.editEntity(entity, { color: COLOR_ON }); + } + } else if (soundPlaying && soundPlaying.playing) { soundPlaying.setOptions( { volume: volume } ); } From 7750e86c135185cbd7b637e9de295ccb477a0801 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 20 Dec 2016 13:47:58 -0800 Subject: [PATCH 030/118] Only send machine fingerprint when not logged in While at it, no longer grabbing it at all when starting up, as that was just a dev thing. Should consider not sending MAC address too, but we may revisit that later. --- interface/src/Application.cpp | 4 ---- libraries/networking/src/NodeList.cpp | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 691c5051b1..9598e81299 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -66,7 +66,6 @@ #include #include #include -#include #include #include #include @@ -614,9 +613,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - // TODO: This is temporary, while developing - FingerprintUtils::getMachineFingerprint(); - // End TODO auto nodeList = DependencyManager::get(); // Set up a watchdog thread to intentionally crash the application on deadlocks diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 98798ba1b5..4c3ff29270 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -373,9 +373,8 @@ void NodeList::sendDomainServerCheckIn() { packetStream << hardwareAddress; - // add in machine fingerprint - QUuid machineFingerprint = FingerprintUtils::getMachineFingerprint(); - packetStream << machineFingerprint; + auto accountManager = DependencyManager::get(); + packetStream << (accountManager->isLoggedIn() ? QUuid() : FingerprintUtils::getMachineFingerprint()); } // pack our data to send to the domain-server including From 1788d19260c88e29b8336fb9f4519395e0c384ea Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 14:00:25 -0800 Subject: [PATCH 031/118] remove some debug code --- interface/src/Application.cpp | 20 +++++--------------- interface/src/avatar/Avatar.cpp | 2 -- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5ca2e1bc7f..691c5051b1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -570,26 +570,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const QString TEST_SCRIPT = "--testScript"; const QString TRACE_FILE = "--traceFile"; const QStringList args = arguments(); - std::cout << "adebug args.size() = " << args.size() << std::endl; // adebug - for (int i = 0; i < args.size(); ++i) { - std::cout << i << " adebug '" << args.at(i).toStdString() << std::endl; // adebug + for (int i = 0; i < args.size() - 1; ++i) { if (args.at(i) == TEST_SCRIPT) { QString testScriptPath = args.at(i + 1); - std::cout << "adebug given test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); - std::cout << "adebug found test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug - ++i; - } else { - std::cout << "adebug did NOT find test script '" << testScriptPath.toStdString() << "'" << std::endl; // adebug - } - } else { - std::cout << "adebug test script not specified'" << std::endl; // adebug - if (args.at(i) == TRACE_FILE) { - QString traceFilePath = args.at(i + 1); - setProperty(hifi::properties::TRACING, traceFilePath); - DependencyManager::get()->startTracing(); } + } else if (args.at(i) == TRACE_FILE) { + QString traceFilePath = args.at(i + 1); + setProperty(hifi::properties::TRACING, traceFilePath); + DependencyManager::get()->startTracing(); } } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 37840436fb..43ba8060f5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -643,7 +643,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { -#ifdef FOO // adebug _attachmentsToDelete.clear(); // check to see if when we added our models to the scene they were ready, if they were not ready, then @@ -667,7 +666,6 @@ void Avatar::fixupModelsInScene() { _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); -#endif // adebug } bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { From 991a61b7222b9977314d68ec0a06e6b6cb0947e6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 14:01:03 -0800 Subject: [PATCH 032/118] fix data collection for AvatarsPerSec --- interface/src/avatar/AvatarManager.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index fa695d9287..88eda5f13a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -129,8 +129,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { QReadLocker lock(&_hashLock); if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { - PROFILE_COUNTER(simulation_avatar, "AvatarsPerSec", { { "aps", 0.0f } }); - PROFILE_COUNTER(simulation_avatar, "JointsPerSec", { { "jps", 0.0f } }); return; } @@ -168,9 +166,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - float avatarsPerSecond = (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start); - PROFILE_COUNTER(simulation_avatar, "AvatarsPerSec", { { "aps", avatarsPerSecond } }); - PROFILE_COUNTER(simulation_avatar, "JointsPerSec", { { "jps", Avatar::getNumJointsProcessedPerSecond() } }); + + PROFILE_COUNTER(app, "AvatarsPerSec", { { "aps", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); + PROFILE_COUNTER(app, "JointsPerSec", { { "jps", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { From 938734d3fc3b5ac3b00da8a330f34695d80d8c95 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 19 Dec 2016 15:10:03 -0800 Subject: [PATCH 033/118] Grip controllers can grab objects, slight delay on search beam --- .../system/controllers/handControllerGrab.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 0cf46f43b0..ee810108c2 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1118,19 +1118,18 @@ function MyController(hand) { }; this.off = function(deltaTime, timestamp) { - if (this.triggerSmoothedReleased()) { + + if (this.triggerSmoothedReleased() && this.secondaryReleased()) { this.waitForTriggerRelease = false; } - if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) { + if (!this.waitForTriggerRelease && (this.triggerSmoothedSqueezed() || this.secondarySqueezed())) { this.lastPickTime = 0; this.startingHandRotation = getControllerWorldLocation(this.handToController(), true).orientation; - if (this.triggerSmoothedSqueezed()) { - this.setState(STATE_SEARCHING, "trigger squeeze detected"); - return; - } + this.searchStartTime = Date.now(); + this.setState(STATE_SEARCHING, "trigger squeeze detected"); + return; } - var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; @@ -1469,6 +1468,9 @@ function MyController(hand) { this.search = function(deltaTime, timestamp) { var _this = this; var name; + var FAR_SEARCH_DELAY = 350; // msecs before search beam appears + + var farSearching = this.triggerSmoothedSqueezed() && (Date.now() - this.searchStartTime > FAR_SEARCH_DELAY); this.grabbedEntity = null; this.grabbedOverlay = null; @@ -1477,7 +1479,7 @@ function MyController(hand) { this.checkForStrayChildren(); - if (this.triggerSmoothedReleased()) { + if ((this.triggerSmoothedReleased() && this.secondaryReleased())) { this.setState(STATE_OFF, "trigger released"); return; } @@ -1496,7 +1498,7 @@ function MyController(hand) { var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateHotSpotEntities); if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab() && holdEnabled) { + if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && holdEnabled) { this.grabbedHotspot = potentialEquipHotspot; this.grabbedEntity = potentialEquipHotspot.entityID; this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); @@ -1539,7 +1541,8 @@ function MyController(hand) { // potentialNearTriggerEntity = entity; } } else { - if (this.triggerSmoothedGrab() && nearGrabEnabled) { + // If near something grabbable, grab it! + if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { var props = entityPropertiesCache.getProps(entity); var grabProps = entityPropertiesCache.getGrabProps(entity); var refCount = grabProps.refCount ? grabProps.refCount : 0; @@ -1629,7 +1632,7 @@ function MyController(hand) { // potentialFarTriggerEntity = entity; } } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled) { + if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { this.grabbedEntity = entity; this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); return; @@ -1706,7 +1709,7 @@ function MyController(hand) { equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); } - if (farGrabEnabled) { + if (farGrabEnabled && farSearching) { this.searchIndicatorOn(rayPickInfo.searchRay); } Reticle.setVisible(false); @@ -2145,7 +2148,7 @@ function MyController(hand) { this.grabPointSphereOff(); - if (this.state == STATE_NEAR_GRABBING && !this.triggerClicked) { + if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); return; @@ -2648,7 +2651,7 @@ function MyController(hand) { this.grabbedOverlay = null; this.grabbedHotspot = null; - if (this.triggerSmoothedGrab()) { + if (this.triggerSmoothedGrab() || this.secondarySqueezed()) { this.waitForTriggerRelease = true; } }; From 250d0717884008c993c6ee7317df21e387ad0aad Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 19 Dec 2016 17:05:34 -0800 Subject: [PATCH 034/118] grip squeezes fingers too --- scripts/system/controllers/squeezeHands.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/squeezeHands.js b/scripts/system/controllers/squeezeHands.js index f06529f2b2..1e94c29521 100644 --- a/scripts/system/controllers/squeezeHands.js +++ b/scripts/system/controllers/squeezeHands.js @@ -53,9 +53,9 @@ function animStateHandler(props) { } function update(dt) { - var leftTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.LT)); - var rightTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.RT)); - + var leftTrigger = clamp(Controller.getValue(Controller.Standard.LT) + Controller.getValue(Controller.Standard.LeftGrip), 0, 1); + var rightTrigger = clamp(Controller.getValue(Controller.Standard.RT) + Controller.getValue(Controller.Standard.RightGrip), 0, 1); + // Average last few trigger values together for a bit of smoothing var tau = clamp(dt / TRIGGER_SMOOTH_TIMESCALE, 0, 1); lastLeftTrigger = lerp(leftTrigger, lastLeftTrigger, tau); From 13e03b35cc39b0498230e910de8d92278c22fe47 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Dec 2016 13:23:28 -0800 Subject: [PATCH 035/118] removed initial delay for seek beams --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ee810108c2..f75fcd1e9b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1468,7 +1468,7 @@ function MyController(hand) { this.search = function(deltaTime, timestamp) { var _this = this; var name; - var FAR_SEARCH_DELAY = 350; // msecs before search beam appears + var FAR_SEARCH_DELAY = 0; // msecs before search beam appears var farSearching = this.triggerSmoothedSqueezed() && (Date.now() - this.searchStartTime > FAR_SEARCH_DELAY); From 0448023a0f2b05f8349b3ec6d88a83e59e1e0260 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Dec 2016 14:14:28 -0800 Subject: [PATCH 036/118] better handle null senders, trim lines after 2000 --- libraries/script-engine/src/ScriptEngines.cpp | 15 ++++++++++----- scripts/developer/debugging/debugWindow.qml | 7 +++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 93c9823920..74acb2ae6a 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -34,27 +34,32 @@ ScriptsModel& getScriptsModel() { void ScriptEngines::onPrintedMessage(const QString& message) { auto scriptEngine = qobject_cast(sender()); - emit printedMessage(message, scriptEngine->getFilename()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit printedMessage(message, scriptName); } void ScriptEngines::onErrorMessage(const QString& message) { auto scriptEngine = qobject_cast(sender()); - emit errorMessage(message, scriptEngine->getFilename()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit errorMessage(message, scriptName); } void ScriptEngines::onWarningMessage(const QString& message) { auto scriptEngine = qobject_cast(sender()); - emit warningMessage(message, scriptEngine->getFilename()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit warningMessage(message, scriptName); } void ScriptEngines::onInfoMessage(const QString& message) { auto scriptEngine = qobject_cast(sender()); - emit infoMessage(message, scriptEngine->getFilename()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit infoMessage(message, scriptName); } void ScriptEngines::onErrorLoadingScript(const QString& url) { auto scriptEngine = qobject_cast(sender()); - emit errorLoadingScript(url, scriptEngine->getFilename()); + auto scriptName = scriptEngine ? scriptEngine->getFilename() : ""; + emit errorLoadingScript(url, scriptName); } ScriptEngines::ScriptEngines() diff --git a/scripts/developer/debugging/debugWindow.qml b/scripts/developer/debugging/debugWindow.qml index 1e012ade6b..f046a949ef 100644 --- a/scripts/developer/debugging/debugWindow.qml +++ b/scripts/developer/debugging/debugWindow.qml @@ -28,6 +28,13 @@ Rectangle { } function fromScript(message) { + var MAX_LINE_COUNT = 2000; + var TRIM_LINES = 500; + if (textArea.lineCount > MAX_LINE_COUNT) { + var lines = textArea.text.split('\n'); + lines.splice(0, TRIM_LINES); + textArea.text = lines.join('\n'); + } textArea.append(message); } } From 0a024616cc1ca939fb99ddf5e95279898fcc303e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Dec 2016 14:21:23 -0800 Subject: [PATCH 037/118] add special equip for oculus style grab --- .../system/controllers/handControllerGrab.js | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f75fcd1e9b..d17ff0582e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -823,7 +823,7 @@ function MyController(hand) { if (this.hand === RIGHT_HAND && this.state === STATE_SEARCHING && this.getOtherHandController().state === STATE_SEARCHING) { this.maybeScaleMyAvatar(); } - + if (this.ignoreInput()) { this.turnOffVisualizations(); return; @@ -1063,11 +1063,16 @@ function MyController(hand) { this.secondaryPress = function(value) { _this.rawSecondaryValue = value; - // The value to check if we will allow the release function to be called - var allowReleaseValue = 0.1; - if (value > 0 && _this.state == STATE_HOLD) { - _this.release(); + if (_this.state == STATE_HOLD && !this.secondaryHeldEquip) { + // when using the index trigger click equip + // any click on the middle finger trigger should release + // check if the middle finger trigger is pressed past the threshold + var allowReleaseValue = 0.1; + if (value > allowReleaseValue) { + _this.release(); + } } + }; this.updateSmoothedTrigger = function() { @@ -1095,6 +1100,12 @@ function MyController(hand) { this.secondaryReleased = function() { return _this.rawSecondaryValue < BUMPER_ON_VALUE; + + if (_this.state == STATE_HOLD && this.secondaryHeldEquip) { + // when using the middle finger hold equip, the equip is + // dropped when the middle finger trigger is released + _this.release(); + } }; // this.triggerOrsecondarySqueezed = function () { @@ -1502,6 +1513,10 @@ function MyController(hand) { this.grabbedHotspot = potentialEquipHotspot; this.grabbedEntity = potentialEquipHotspot.entityID; this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); + + // handle the Oculus Touch equip where the middle finger trigger is held + this.secondaryHeldEquip = this.secondarySqueezed && !this.triggerSmoothedGrab; + return; } } From 8cd13eaedf84ed8d9517ff94a88c206806c22191 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Dec 2016 14:25:26 -0800 Subject: [PATCH 038/118] always unequip on secondary release --- .../system/controllers/handControllerGrab.js | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d17ff0582e..628d568ddd 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1062,17 +1062,6 @@ function MyController(hand) { this.secondaryPress = function(value) { _this.rawSecondaryValue = value; - - if (_this.state == STATE_HOLD && !this.secondaryHeldEquip) { - // when using the index trigger click equip - // any click on the middle finger trigger should release - // check if the middle finger trigger is pressed past the threshold - var allowReleaseValue = 0.1; - if (value > allowReleaseValue) { - _this.release(); - } - } - }; this.updateSmoothedTrigger = function() { @@ -1101,9 +1090,9 @@ function MyController(hand) { this.secondaryReleased = function() { return _this.rawSecondaryValue < BUMPER_ON_VALUE; - if (_this.state == STATE_HOLD && this.secondaryHeldEquip) { - // when using the middle finger hold equip, the equip is - // dropped when the middle finger trigger is released + if (_this.state == STATE_HOLD) { + // if we were holding something, the release of the + // secondary trigger releases it _this.release(); } }; @@ -1514,9 +1503,6 @@ function MyController(hand) { this.grabbedEntity = potentialEquipHotspot.entityID; this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); - // handle the Oculus Touch equip where the middle finger trigger is held - this.secondaryHeldEquip = this.secondarySqueezed && !this.triggerSmoothedGrab; - return; } } From 4a25b727d88927c1b0294c3dae3dfd97c00734e8 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 20 Dec 2016 14:26:45 -0800 Subject: [PATCH 039/118] Add comment back, with interest --- libraries/networking/src/NodeList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 4c3ff29270..4606c44cd1 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -373,6 +373,7 @@ void NodeList::sendDomainServerCheckIn() { packetStream << hardwareAddress; + // now add the machine fingerprint - a null UUID if logged in, real one if not logged in auto accountManager = DependencyManager::get(); packetStream << (accountManager->isLoggedIn() ? QUuid() : FingerprintUtils::getMachineFingerprint()); } From c3d736dd16c1543413c523827ae3a6d36a34368a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Dec 2016 14:30:08 -0800 Subject: [PATCH 040/118] return after release, duh --- scripts/system/controllers/handControllerGrab.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 628d568ddd..5f42882194 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1088,13 +1088,15 @@ function MyController(hand) { }; this.secondaryReleased = function() { - return _this.rawSecondaryValue < BUMPER_ON_VALUE; + var released = _this.rawSecondaryValue < BUMPER_ON_VALUE; - if (_this.state == STATE_HOLD) { + if (released) { // if we were holding something, the release of the // secondary trigger releases it _this.release(); } + + return released; }; // this.triggerOrsecondarySqueezed = function () { From 4fcccb545559e741f159890882b09b19870c8b5c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Dec 2016 14:32:47 -0800 Subject: [PATCH 041/118] check for release of equip in this.nearGrabbing --- .../system/controllers/handControllerGrab.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5f42882194..040b083a69 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1088,15 +1088,7 @@ function MyController(hand) { }; this.secondaryReleased = function() { - var released = _this.rawSecondaryValue < BUMPER_ON_VALUE; - - if (released) { - // if we were holding something, the release of the - // secondary trigger releases it - _this.release(); - } - - return released; + return _this.rawSecondaryValue < BUMPER_ON_VALUE; }; // this.triggerOrsecondarySqueezed = function () { @@ -2159,6 +2151,13 @@ function MyController(hand) { if (this.state == STATE_HOLD) { + if (this.secondaryReleased()) { + // we have an equipped object and the secondary trigger was released + // short-circuit the other checks and release it + this.release() + return; + } + var dropDetected = this.dropGestureProcess(deltaTime); if (this.triggerSmoothedReleased()) { From 4d711d869c35ec7c49d1154382cc7210a7bc1b14 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 14:45:11 -0800 Subject: [PATCH 042/118] cleanup --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 43ba8060f5..2f99ccfdc8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -294,7 +294,7 @@ void Avatar::updateAvatarEntities() { void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - if (!_motionState && !isDead()) { + if (!isDead() && !_motionState) { DependencyManager::get()->addAvatarToSimulation(this); } animateScaleChanges(deltaTime); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 88eda5f13a..b25968433d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -167,8 +167,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - PROFILE_COUNTER(app, "AvatarsPerSec", { { "aps", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); - PROFILE_COUNTER(app, "JointsPerSec", { { "jps", Avatar::getNumJointsProcessedPerSecond() } }); + PROFILE_COUNTER(app, "NumAvatarsPerSec", + { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); + PROFILE_COUNTER(app, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { From ebd347fc15e683d6b57ae66a07e31f5dc3460d76 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Dec 2016 14:46:55 -0800 Subject: [PATCH 043/118] only respect release of secondary for hold once pressed --- scripts/system/controllers/handControllerGrab.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 040b083a69..1f536b9567 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1470,6 +1470,7 @@ function MyController(hand) { this.grabbedOverlay = null; this.isInitialGrab = false; this.shouldResetParentOnRelease = false; + this.preparingHoldRelease = false; this.checkForStrayChildren(); @@ -2151,10 +2152,19 @@ function MyController(hand) { if (this.state == STATE_HOLD) { - if (this.secondaryReleased()) { + if (this.secondarySqueezed()) { + // this.secondaryReleased() will always be true when not depressed + // so we cannot simply rely on that for release - ensure that the + // trigger was first "prepared" by being pushed in before the release + this.preparingHoldRelease = true; + } + + if (this.preparingHoldRelease && this.secondaryReleased()) { // we have an equipped object and the secondary trigger was released // short-circuit the other checks and release it - this.release() + this.preparingHoldRelease = false; + + this.release(); return; } From fa18d548f7273cf0e94fd89dec587d08a6c155a2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 15:28:25 -0800 Subject: [PATCH 044/118] use proper category for NumAvatarsPerSec trace --- interface/src/avatar/AvatarManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b25968433d..ee92bc9210 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -167,9 +167,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - PROFILE_COUNTER(app, "NumAvatarsPerSec", + PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); - PROFILE_COUNTER(app, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); + PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { From 4bade4400f8f1ff45b3c12f54285400b01cf8b89 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Dec 2016 16:02:26 -0800 Subject: [PATCH 045/118] fixed typo per review --- scripts/tutorials/entity_scripts/ambientSound.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index b3ec919078..6a892ed139 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -4,7 +4,7 @@ // range of this entity. Great way to add one or more ambisonic soundfields to your environment. // // In the userData section for the entity, add/edit three values: -// userData.SoundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. +// userData.soundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. // userData.range should be an integer for the max distance away from the entity where the sound will be audible. // userData.volume is the max volume at which the clip should play. Defaults to 1.0 full volume) // From 3f4faec15ff7aef8e0971e12dc47ee3e158f52fd Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 20 Dec 2016 16:14:59 -0800 Subject: [PATCH 046/118] Fix idententity packet in AC --- libraries/avatars/src/AvatarHashMap.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index ffb945df4c..241568aab3 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -134,11 +134,16 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // make sure this isn't for an ignored avatar auto nodeList = DependencyManager::get(); static auto EMPTY = QUuid(); - if (identity.uuid == _avatarHash.value(EMPTY)->getSessionUUID()) { - // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an - // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), - // we make things match here. - identity.uuid = EMPTY; + + { + QWriteLocker locker(&_hashLock); + auto me = _avatarHash.find(EMPTY); + if ((me != _avatarHash.end()) && (identity.uuid == me.value()->getSessionUUID())) { + // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an + // identity packet for ourself (such as when we are assigned a sessionDisplayName by the mixer upon joining), + // we make things match here. + identity.uuid = EMPTY; + } } if (!nodeList->isIgnoringNode(identity.uuid)) { // mesh URL for a UUID, find avatar in our list From e43efa74de0708dffbbaf49e0548e87013029a82 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 16:16:31 -0800 Subject: [PATCH 047/118] use sampled counter --- interface/src/avatar/AvatarManager.cpp | 7 +++++-- libraries/shared/src/Profile.cpp | 1 - libraries/shared/src/Profile.h | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ee92bc9210..53f17e9635 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -124,6 +124,9 @@ void AvatarManager::updateMyAvatar(float deltaTime) { } } + +Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar"); + void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); @@ -167,9 +170,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", + SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumAvatarsPerSec", { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); - PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); + SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 33c5c15862..4c5882348e 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -18,7 +18,6 @@ Q_LOGGING_CATEGORY(trace_resource_network, "trace.resource.network") Q_LOGGING_CATEGORY(trace_resource_parse, "trace.resource.parse") Q_LOGGING_CATEGORY(trace_simulation, "trace.simulation") Q_LOGGING_CATEGORY(trace_simulation_animation, "trace.simulation.animation") -Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar") Q_LOGGING_CATEGORY(trace_simulation_physics, "trace.simulation.physics") #if defined(NSIGHT_FOUND) diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index 57a98deb73..6d02a0939f 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -22,7 +22,6 @@ Q_DECLARE_LOGGING_CATEGORY(trace_resource_parse) Q_DECLARE_LOGGING_CATEGORY(trace_resource_network) Q_DECLARE_LOGGING_CATEGORY(trace_simulation) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_animation) -Q_DECLARE_LOGGING_CATEGORY(trace_simulation_avatar) Q_DECLARE_LOGGING_CATEGORY(trace_simulation_physics) class Duration { From a6e353d75f034005437297e3eb3aa9c21d49b743 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 20 Dec 2016 16:25:23 -0800 Subject: [PATCH 048/118] read lock, not write --- libraries/avatars/src/AvatarHashMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 241568aab3..8871769261 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -136,7 +136,7 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer static auto EMPTY = QUuid(); { - QWriteLocker locker(&_hashLock); + QReadLocker locker(&_hashLock); auto me = _avatarHash.find(EMPTY); if ((me != _avatarHash.end()) && (identity.uuid == me.value()->getSessionUUID())) { // We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an From 5e65e3991cc5c7fc17e81137a40ff7a35cc0d280 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Dec 2016 21:03:10 -0800 Subject: [PATCH 049/118] CR feedback --- assignment-client/src/Agent.cpp | 3 ++- assignment-client/src/avatars/AvatarMixer.cpp | 10 ++++++---- .../src/avatars/AvatarMixerClientData.cpp | 4 ---- .../src/avatars/AvatarMixerClientData.h | 1 - interface/src/avatar/MyAvatar.cpp | 6 +++--- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 12 ++++++++---- libraries/avatars/src/AvatarData.h | 9 ++++++++- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index dc6b2427f2..e79085244f 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -499,7 +499,8 @@ void Agent::processAgentAvatar() { if (!_scriptEngine->isFinished() && _isAvatar) { auto scriptedAvatar = DependencyManager::get(); - QByteArray avatarByteArray = scriptedAvatar->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + QByteArray avatarByteArray = scriptedAvatar->toByteArray((randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO) + ? AvatarData::SendAllData : AvatarData::CullSmallData); scriptedAvatar->doneEncoding(true); static AvatarDataSequenceNumber sequenceNumber = 0; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 540526524e..d8d0b10fea 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -386,17 +386,19 @@ void AvatarMixer::broadcastAvatarData() { // determine if avatar is in view, to determine how much data to include... glm::vec3 otherNodeBoxScale = (otherNodeData->getPosition() - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - bool sendMinimumForOutOfView = !nodeData->otherAvatarInView(otherNodeBox); - if (sendMinimumForOutOfView) { + AvatarData::AvatarDataDetail detail; + if (!nodeData->otherAvatarInView(otherNodeBox)) { + detail = AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); } else { + detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO + ? AvatarData::SendAllData : AvatarData::IncludeSmallData; nodeData->incrementAvatarInView(); } numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += - avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO, sendMinimumForOutOfView)); + numAvatarDataBytes += avatarPacketList->write(otherAvatar.toByteArray(detail)); avatarPacketList->endSegment(); }); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 162ef2c462..e6e2fc3849 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -62,10 +62,6 @@ void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { _currentViewFrustum.fromByteArray(message); } -bool AvatarMixerClientData::otherAvatarInView(const glm::vec3& otherAvatar) { - return !_currentViewFrustumIsValid || _currentViewFrustum.pointIntersectsFrustum(otherAvatar); -} - bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 6b916750b4..b894932ba7 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -95,7 +95,6 @@ public: void readViewFrustumPacket(const QByteArray& message); bool otherAvatarInView(const AABox& otherAvatarBox); - bool otherAvatarInView(const glm::vec3& otherAvatar); void resetInViewStats() { _recentOtherAvatarsInView = _recentOtherAvatarsOutOfView = 0; } void incrementAvatarInView() { _recentOtherAvatarsInView++; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9547d769fa..eebcee8e4c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -226,7 +226,7 @@ void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() } -QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) { +QByteArray MyAvatar::toByteArray(AvatarDataDetail dataDetail) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); _globalBoundingBoxCorner.x = _characterController.getCapsuleRadius(); @@ -237,12 +237,12 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendM // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getPosition(); setPosition(getSkeletonPosition()); - QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum); + QByteArray array = AvatarData::toByteArray(dataDetail); // copy the correct position back setPosition(oldPosition); return array; } - return AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum); + return AvatarData::toByteArray(dataDetail); } void MyAvatar::centerBody() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 14770fa14f..0e5ce0fe7b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -333,7 +333,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) override; + QByteArray toByteArray(AvatarDataDetail dataDetail) override; void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 73df7a2f9e..d77f70fbe9 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -181,7 +181,11 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition()); } -QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum) { + +QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail) { + bool cullSmallChanges = (dataDetail == CullSmallData); + bool sendAll = (dataDetail == SendAllData); + bool sendMinimum = (dataDetail == MinimumData); // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -1208,9 +1212,9 @@ void AvatarData::sendAvatarDataPacket() { // about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed. // this is to guard against a joint moving once, the packet getting lost, and the joint never moving again. - bool sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO; - QByteArray avatarByteArray = toByteArray(true, sendFullUpdate); - doneEncoding(true); + QByteArray avatarByteArray = toByteArray((randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO) ? SendAllData : CullSmallData); + + doneEncoding(true); // FIXME - doneEncoding() takes a bool for culling small changes, that's janky! static AvatarDataSequenceNumber sequenceNumber = 0; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1ad6d32810..db06d52092 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -209,7 +209,14 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false); + typedef enum { + MinimumData, + CullSmallData, + IncludeSmallData, + SendAllData + } AvatarDataDetail; + + virtual QByteArray toByteArray(AvatarDataDetail dataDetail); virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged From 2ba4fee44bd2afbd14812863ec672319683e032c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Dec 2016 08:26:20 -0800 Subject: [PATCH 050/118] CR feedback --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 4 +--- assignment-client/src/avatars/AvatarMixerClientData.h | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index e6e2fc3849..c65703b8e6 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -58,15 +58,13 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe } void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { - _currentViewFrustumIsValid = true; _currentViewFrustum.fromByteArray(message); } bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { - return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); + return _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); } - void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index b894932ba7..78a30d8206 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -35,7 +35,7 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; class AvatarMixerClientData : public NodeData { Q_OBJECT public: - AvatarMixerClientData(const QUuid& nodeID = QUuid()) : NodeData(nodeID) {} + AvatarMixerClientData(const QUuid& nodeID = QUuid()) : NodeData(nodeID) { _currentViewFrustum.invalidate(); } virtual ~AvatarMixerClientData() {} using HRCTime = p_high_resolution_clock::time_point; @@ -125,7 +125,6 @@ private: SimpleMovingAverage _avgOtherAvatarDataRate; std::unordered_set _radiusIgnoredOthers; ViewFrustum _currentViewFrustum; - bool _currentViewFrustumIsValid { false }; int _recentOtherAvatarsInView { 0 }; int _recentOtherAvatarsOutOfView { 0 }; From 8ab6974233572515f588a4ab148f6287ac60366d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 15 Dec 2016 14:32:55 -0800 Subject: [PATCH 051/118] optimizations for processing avatar joint data --- interface/src/Application.cpp | 11 ++++- interface/src/avatar/Avatar.cpp | 82 ++++++++++++++++++++------------- libraries/animation/src/Rig.cpp | 73 ++++++++++++++++++----------- libraries/animation/src/Rig.h | 1 + 4 files changed, 106 insertions(+), 61 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b4b0ad10bb..e32e9e90b1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2572,6 +2572,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { } static bool _altPressed{ false }; +static bool dumpAllTimerRecords { false }; // adebug hack void Application::keyPressEvent(QKeyEvent* event) { _altPressed = event->key() == Qt::Key_Alt; @@ -2662,7 +2663,8 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_F: { - _physicsEngine->dumpNextStats(); + //_physicsEngine->dumpNextStats(); + dumpAllTimerRecords = true; // adebug hack break; } @@ -3408,6 +3410,11 @@ void Application::idle(float nsecsElapsed) { } _overlayConductor.update(secondsSinceLastUpdate); + // adebug hack + if (dumpAllTimerRecords) { + PerformanceTimer::dumpAllTimerRecords(); + dumpAllTimerRecords = false; + } } void Application::setLowVelocityFilter(bool lowVelocityFilter) { @@ -4307,7 +4314,7 @@ void Application::update(float deltaTime) { // AvatarManager update { - PerformanceTimer perfTimer("AvatarManger"); + PerformanceTimer perfTimer("AvatarManager"); _avatarSimCounter.increment(); { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2f99ccfdc8..908b3def8d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -275,13 +275,16 @@ void Avatar::updateAvatarEntities() { } AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); - _avatarEntitiesLock.withReadLock([&] { - foreach (auto entityID, recentlyDettachedAvatarEntities) { - if (!_avatarEntityData.contains(entityID)) { - entityTree->deleteEntity(entityID, true, true); + if (!recentlyDettachedAvatarEntities.empty()) { + // only lock this thread when absolutely necessary + _avatarEntitiesLock.withReadLock([&] { + foreach (auto entityID, recentlyDettachedAvatarEntities) { + if (!_avatarEntityData.contains(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } } - } - }); + }); + } }); if (success) { @@ -299,18 +302,25 @@ void Avatar::simulate(float deltaTime) { } animateScaleChanges(deltaTime); - bool avatarPositionInView = false; - bool avatarMeshInView = false; + bool avatarInView = false; { // update the shouldAnimate flag to match whether or not we will render the avatar. PerformanceTimer perfTimer("cull"); - ViewFrustum viewFrustum; { - PerformanceTimer perfTimer("LOD"); + // simple frustum check + PerformanceTimer perfTimer("inView"); + ViewFrustum viewFrustum; + qApp->copyDisplayViewFrustum(viewFrustum); + avatarInView = viewFrustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius()) + || viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound()); + } + PerformanceTimer lodPerfTimer("LOD"); + if (avatarInView) { const float MINIMUM_VISIBILITY_FOR_ON = 0.4f; const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f; + ViewFrustum viewFrustum; qApp->copyViewFrustum(viewFrustum); float visibility = calculateRenderAccuracy(viewFrustum.getPosition(), - getBounds(), DependencyManager::get()->getOctreeSizeScale()); + getBounds(), DependencyManager::get()->getOctreeSizeScale()); if (!_shouldAnimate) { if (visibility > MINIMUM_VISIBILITY_FOR_ON) { _shouldAnimate = true; @@ -321,19 +331,11 @@ void Avatar::simulate(float deltaTime) { qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility; } } - - { - PerformanceTimer perfTimer("inView"); - // simple frustum check - float boundingRadius = getBoundingRadius(); - qApp->copyDisplayViewFrustum(viewFrustum); - avatarPositionInView = viewFrustum.sphereIntersectsFrustum(getPosition(), boundingRadius); - avatarMeshInView = viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound()); - } } uint64_t start = usecTimestampNow(); - if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) { + // CRUFT? _shouldSkipRender is never set 'true' + if (_shouldAnimate && avatarInView && !_shouldSkipRender) { { PerformanceTimer perfTimer("skeleton"); _skeletonModel->getRig()->copyJointsFromJointData(_jointData); @@ -643,6 +645,8 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { +#define ADEBUG +#ifdef ADEBUG _attachmentsToDelete.clear(); // check to see if when we added our models to the scene they were ready, if they were not ready, then @@ -666,6 +670,7 @@ void Avatar::fixupModelsInScene() { _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); +#endif // ADEBUG } bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { @@ -725,7 +730,7 @@ glm::vec3 Avatar::getDisplayNamePosition() const { glm::vec3 bodyUpDirection = getBodyUpDirection(); DEBUG_VALUE("bodyUpDirection =", bodyUpDirection); - if (getSkeletonModel()->getNeckPosition(namePosition)) { + if (_skeletonModel->getNeckPosition(namePosition)) { float headHeight = getHeadHeight(); DEBUG_VALUE("namePosition =", namePosition); DEBUG_VALUE("headHeight =", headHeight); @@ -1244,8 +1249,8 @@ glm::vec3 Avatar::getUncachedLeftPalmPosition() const { return leftPalmPosition; } // avatar didn't have a LeftHandMiddle1 joint, fall back on this: - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getLeftHandJointIndex(), leftPalmRotation); - getSkeletonModel()->getLeftHandPosition(leftPalmPosition); + _skeletonModel->getJointRotationInWorldFrame(_skeletonModel->getLeftHandJointIndex(), leftPalmRotation); + _skeletonModel->getLeftHandPosition(leftPalmPosition); leftPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftPalmRotation); return leftPalmPosition; } @@ -1253,7 +1258,7 @@ glm::vec3 Avatar::getUncachedLeftPalmPosition() const { glm::quat Avatar::getUncachedLeftPalmRotation() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat leftPalmRotation; - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getLeftHandJointIndex(), leftPalmRotation); + _skeletonModel->getJointRotationInWorldFrame(_skeletonModel->getLeftHandJointIndex(), leftPalmRotation); return leftPalmRotation; } @@ -1265,8 +1270,8 @@ glm::vec3 Avatar::getUncachedRightPalmPosition() const { return rightPalmPosition; } // avatar didn't have a RightHandMiddle1 joint, fall back on this: - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getRightHandJointIndex(), rightPalmRotation); - getSkeletonModel()->getRightHandPosition(rightPalmPosition); + _skeletonModel->getJointRotationInWorldFrame(_skeletonModel->getRightHandJointIndex(), rightPalmRotation); + _skeletonModel->getRightHandPosition(rightPalmPosition); rightPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightPalmRotation); return rightPalmPosition; } @@ -1274,7 +1279,7 @@ glm::vec3 Avatar::getUncachedRightPalmPosition() const { glm::quat Avatar::getUncachedRightPalmRotation() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat rightPalmRotation; - getSkeletonModel()->getJointRotationInWorldFrame(getSkeletonModel()->getRightHandJointIndex(), rightPalmRotation); + _skeletonModel->getJointRotationInWorldFrame(_skeletonModel->getRightHandJointIndex(), rightPalmRotation); return rightPalmRotation; } @@ -1291,10 +1296,23 @@ void Avatar::setOrientation(const glm::quat& orientation) { void Avatar::updatePalms() { PerformanceTimer perfTimer("palms"); // update thread-safe caches - _leftPalmRotationCache.set(getUncachedLeftPalmRotation()); - _rightPalmRotationCache.set(getUncachedRightPalmRotation()); - _leftPalmPositionCache.set(getUncachedLeftPalmPosition()); - _rightPalmPositionCache.set(getUncachedRightPalmPosition()); + glm::quat rotation; + int i = _skeletonModel->getLeftHandJointIndex(); + if (_skeletonModel->getJointRotationInWorldFrame(i, rotation)) { + _leftPalmRotationCache.set(rotation); + } + glm::vec3 position; + if (_skeletonModel->getJointPositionInWorldFrame(i, position)) { + _leftPalmPositionCache.set(position); + } + + i = _skeletonModel->getRightHandJointIndex(); + if (_skeletonModel->getJointRotationInWorldFrame(i, rotation)) { + _rightPalmRotationCache.set(rotation); + } + if (_skeletonModel->getJointPositionInWorldFrame(i, position)) { + _rightPalmPositionCache.set(position); + } } void Avatar::setParentID(const QUuid& parentID) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 886eafffcf..85e2fceb9b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -161,6 +161,7 @@ void Rig::destroyAnimGraph() { _internalPoseSet._absolutePoses.clear(); _internalPoseSet._overridePoses.clear(); _internalPoseSet._overrideFlags.clear(); + _numOverrides = 0; } void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { @@ -180,6 +181,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); @@ -207,6 +209,7 @@ void Rig::reset(const FBXGeometry& geometry) { _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); @@ -276,13 +279,17 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { void Rig::clearJointState(int index) { if (isIndexValid(index)) { - _internalPoseSet._overrideFlags[index] = false; + if (_internalPoseSet._overrideFlags[index]) { + _internalPoseSet._overrideFlags[index] = false; + --_numOverrides; + } _internalPoseSet._overridePoses[index] = _animSkeleton->getRelativeDefaultPose(index); } } void Rig::clearJointStates() { _internalPoseSet._overrideFlags.clear(); + _numOverrides = 0; if (_animSkeleton) { _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints()); _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -291,7 +298,10 @@ void Rig::clearJointStates() { void Rig::clearJointAnimationPriority(int index) { if (isIndexValid(index)) { - _internalPoseSet._overrideFlags[index] = false; + if (_internalPoseSet._overrideFlags[index]) { + _internalPoseSet._overrideFlags[index] = false; + --_numOverrides; + } _internalPoseSet._overridePoses[index] = _animSkeleton->getRelativeDefaultPose(index); } } @@ -320,7 +330,10 @@ void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translatio if (isIndexValid(index)) { if (valid) { assert(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size()); - _internalPoseSet._overrideFlags[index] = true; + if (!_internalPoseSet._overrideFlags[index]) { + _internalPoseSet._overrideFlags[index] = true; + ++_numOverrides; + } _internalPoseSet._overridePoses[index].trans = translation; } } @@ -329,7 +342,10 @@ void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translatio void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (isIndexValid(index)) { assert(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size()); - _internalPoseSet._overrideFlags[index] = true; + if (!_internalPoseSet._overrideFlags[index]) { + _internalPoseSet._overrideFlags[index] = true; + ++_numOverrides; + } _internalPoseSet._overridePoses[index].rot = rotation; _internalPoseSet._overridePoses[index].trans = translation; } @@ -339,7 +355,10 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo if (isIndexValid(index)) { if (valid) { ASSERT(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size()); - _internalPoseSet._overrideFlags[index] = true; + if (!_internalPoseSet._overrideFlags[index]) { + _internalPoseSet._overrideFlags[index] = true; + ++_numOverrides; + } _internalPoseSet._overridePoses[index].rot = rotation; } } @@ -518,7 +537,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // sine wave LFO var for testing. static float t = 0.0f; - _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); + _animVars.set("sine", 2.0f * 0.5f * sinf(t) + 0.5f); float moveForwardAlpha = 0.0f; float moveBackwardAlpha = 0.0f; @@ -884,10 +903,12 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xffff00ff, 0); + PerformanceTimer perfTimer("updateAnimations"); setModelOffset(rootTransform); if (_animNode) { + PerformanceTimer perfTimer("handleTriggers"); updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); @@ -903,13 +924,13 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { for (auto& trigger : triggersOut) { _animVars.setTrigger(trigger); } + applyOverridePoses(); } - - applyOverridePoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); // copy internal poses to external poses { + PerformanceTimer perfTimer("copy"); QWriteLocker writeLock(&_externalPoseSetLock); _externalPoseSet = _internalPoseSet; } @@ -1176,7 +1197,8 @@ bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const } void Rig::applyOverridePoses() { - if (!_animSkeleton) { + PerformanceTimer perfTimer("override"); + if (!_animSkeleton || _numOverrides == 0) { return; } @@ -1192,28 +1214,26 @@ void Rig::applyOverridePoses() { } void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) { + PerformanceTimer perfTimer("buildAbsolute"); if (!_animSkeleton) { return; } ASSERT(_animSkeleton->getNumJoints() == (int)relativePoses.size()); - // flatten all poses out so they are absolute not relative - absolutePosesOut.resize(relativePoses.size()); - for (int i = 0; i < (int)relativePoses.size(); i++) { - int parentIndex = _animSkeleton->getParentIndex(i); - if (parentIndex == -1) { - absolutePosesOut[i] = relativePoses[i]; - } else { - absolutePosesOut[i] = absolutePosesOut[parentIndex] * relativePoses[i]; + { + absolutePosesOut.resize(relativePoses.size()); + AnimPose geometryToRigTransform(_geometryToRigTransform); + for (int i = 0; i < (int)relativePoses.size(); i++) { + int parentIndex = _animSkeleton->getParentIndex(i); + if (parentIndex == -1) { + // transform all root absolute poses into rig space + absolutePosesOut[i] = geometryToRigTransform * relativePoses[i]; + } else { + absolutePosesOut[i] = absolutePosesOut[parentIndex] * relativePoses[i]; + } } } - - // transform all absolute poses into rig space. - AnimPose geometryToRigTransform(_geometryToRigTransform); - for (int i = 0; i < (int)absolutePosesOut.size(); i++) { - absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i]; - } } glm::mat4 Rig::getJointTransform(int jointIndex) const { @@ -1302,11 +1322,10 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { // copy the geometry space parent relative poses into _overridePoses for (int i = 0; i < jointDataVec.size(); i++) { if (overrideFlags[i]) { - _internalPoseSet._overrideFlags[i] = true; - _internalPoseSet._overridePoses[i].scale = Vectors::ONE; - _internalPoseSet._overridePoses[i].rot = rotations[i]; + _internalPoseSet._relativePoses[i].scale = Vectors::ONE; + _internalPoseSet._relativePoses[i].rot = rotations[i]; // scale translations from meters back into geometry units. - _internalPoseSet._overridePoses[i].trans = _invGeometryOffset.scale * translations[i]; + _internalPoseSet._relativePoses[i].trans = _invGeometryOffset.scale * translations[i]; } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 151a7ae8e9..aa091fe10c 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -311,6 +311,7 @@ protected: std::map _origRoleAnimations; + int32_t _numOverrides { 0 }; bool _lastEnableInverseKinematics { true }; bool _enableInverseKinematics { true }; From 3f687887b9d1cd2ddb69b8fb5b701bf4def07ddd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 15 Dec 2016 15:03:20 -0800 Subject: [PATCH 052/118] faster math when unpacking JointData rotations --- libraries/animation/src/Rig.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 85e2fceb9b..dfcace1d77 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1274,14 +1274,13 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._overrideFlags.size()) { // transform all the default poses into rig space. - const AnimPose geometryToRigPose(_geometryToRigTransform); std::vector overrideFlags(_internalPoseSet._overridePoses.size(), false); // start with the default rotations in absolute rig frame std::vector rotations; rotations.reserve(_animSkeleton->getAbsoluteDefaultPoses().size()); for (auto& pose : _animSkeleton->getAbsoluteDefaultPoses()) { - rotations.push_back(geometryToRigPose.rot * pose.rot); + rotations.push_back(pose.rot); } // start translations in relative frame but scaled to meters. @@ -1294,12 +1293,14 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { ASSERT(overrideFlags.size() == rotations.size()); // copy over rotations from the jointDataVec, which is also in absolute rig frame + const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); for (int i = 0; i < jointDataVec.size(); i++) { if (isIndexValid(i)) { const JointData& data = jointDataVec.at(i); if (data.rotationSet) { overrideFlags[i] = true; - rotations[i] = data.rotation; + // JointData rotations are in rig frame + rotations[i] = rigToGeometryRot * data.rotation; } if (data.translationSet) { overrideFlags[i] = true; @@ -1310,12 +1311,6 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { ASSERT(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size()); - // convert resulting rotations into geometry space. - const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); - for (auto& rot : rotations) { - rot = rigToGeometryRot * rot; - } - // convert all rotations from absolute to parent relative. _animSkeleton->convertAbsoluteRotationsToRelative(rotations); From b937eff582eeca1372b17f43e3d746dcc6cd002f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 15 Dec 2016 16:03:44 -0800 Subject: [PATCH 053/118] more faster math copying JointData --- libraries/animation/src/Rig.cpp | 60 +++++++++++---------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index dfcace1d77..c6aae824e4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1271,56 +1271,36 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { void Rig::copyJointsFromJointData(const QVector& jointDataVec) { PerformanceTimer perfTimer("copyJoints"); - if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._overrideFlags.size()) { - - // transform all the default poses into rig space. - std::vector overrideFlags(_internalPoseSet._overridePoses.size(), false); - - // start with the default rotations in absolute rig frame + if (_animSkeleton && jointDataVec.size() == (int)_internalPoseSet._relativePoses.size()) { + // make a vector of rotations in absolute-geometry-frame + const AnimPoseVec& absoluteDefaultPoses = _animSkeleton->getAbsoluteDefaultPoses(); std::vector rotations; rotations.reserve(_animSkeleton->getAbsoluteDefaultPoses().size()); - for (auto& pose : _animSkeleton->getAbsoluteDefaultPoses()) { - rotations.push_back(pose.rot); - } - - // start translations in relative frame but scaled to meters. - std::vector translations; - translations.reserve(_animSkeleton->getRelativeDefaultPoses().size()); - for (auto& pose : _animSkeleton->getRelativeDefaultPoses()) { - translations.push_back(_geometryOffset.scale * pose.trans); - } - - ASSERT(overrideFlags.size() == rotations.size()); - - // copy over rotations from the jointDataVec, which is also in absolute rig frame const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); for (int i = 0; i < jointDataVec.size(); i++) { - if (isIndexValid(i)) { - const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { - overrideFlags[i] = true; - // JointData rotations are in rig frame - rotations[i] = rigToGeometryRot * data.rotation; - } - if (data.translationSet) { - overrideFlags[i] = true; - translations[i] = data.translation; - } + const JointData& data = jointDataVec.at(i); + if (data.rotationSet) { + // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame + rotations.push_back(rigToGeometryRot * data.rotation); + } else { + rotations.push_back(absoluteDefaultPoses[i].rot); } } - ASSERT(_internalPoseSet._overrideFlags.size() == _internalPoseSet._overridePoses.size()); - - // convert all rotations from absolute to parent relative. + // convert rotations from absolute to parent relative. _animSkeleton->convertAbsoluteRotationsToRelative(rotations); - // copy the geometry space parent relative poses into _overridePoses + // store new relative poses + const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses(); for (int i = 0; i < jointDataVec.size(); i++) { - if (overrideFlags[i]) { - _internalPoseSet._relativePoses[i].scale = Vectors::ONE; - _internalPoseSet._relativePoses[i].rot = rotations[i]; - // scale translations from meters back into geometry units. - _internalPoseSet._relativePoses[i].trans = _invGeometryOffset.scale * translations[i]; + const JointData& data = jointDataVec.at(i); + _internalPoseSet._relativePoses[i].scale = Vectors::ONE; + _internalPoseSet._relativePoses[i].rot = rotations[i]; + if (data.translationSet) { + // JointData translations are in scaled relative-frame so we scale back to regular relative-frame + _internalPoseSet._relativePoses[i].trans = _invGeometryOffset.scale * data.translation; + } else { + _internalPoseSet._relativePoses[i].trans = relativeDefaultPoses[i].trans; } } } From 383064999051202ff6e61258278bc08ec7e8bfd8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 16 Dec 2016 08:46:11 -0800 Subject: [PATCH 054/118] remove debugging and profiling --- interface/src/avatar/Avatar.cpp | 3 --- libraries/animation/src/Rig.cpp | 7 ++----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 908b3def8d..a79d2372c5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -645,8 +645,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { -#define ADEBUG -#ifdef ADEBUG _attachmentsToDelete.clear(); // check to see if when we added our models to the scene they were ready, if they were not ready, then @@ -670,7 +668,6 @@ void Avatar::fixupModelsInScene() { _attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end()); _attachmentsToRemove.clear(); scene->enqueuePendingChanges(pendingChanges); -#endif // ADEBUG } bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c6aae824e4..b6bb8ccc8f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -929,11 +929,8 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); // copy internal poses to external poses - { - PerformanceTimer perfTimer("copy"); - QWriteLocker writeLock(&_externalPoseSetLock); - _externalPoseSet = _internalPoseSet; - } + QWriteLocker writeLock(&_externalPoseSetLock); + _externalPoseSet = _internalPoseSet; } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, From 2209c0ebbac75a665350ce67eb2f63dd260535a9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 16 Dec 2016 13:10:30 -0800 Subject: [PATCH 055/118] remove debug hook for dumping stats to logs --- interface/src/Application.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e32e9e90b1..d92ba53f6e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2572,7 +2572,6 @@ bool Application::eventFilter(QObject* object, QEvent* event) { } static bool _altPressed{ false }; -static bool dumpAllTimerRecords { false }; // adebug hack void Application::keyPressEvent(QKeyEvent* event) { _altPressed = event->key() == Qt::Key_Alt; @@ -2663,8 +2662,7 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_F: { - //_physicsEngine->dumpNextStats(); - dumpAllTimerRecords = true; // adebug hack + _physicsEngine->dumpNextStats(); break; } @@ -3410,11 +3408,6 @@ void Application::idle(float nsecsElapsed) { } _overlayConductor.update(secondsSinceLastUpdate); - // adebug hack - if (dumpAllTimerRecords) { - PerformanceTimer::dumpAllTimerRecords(); - dumpAllTimerRecords = false; - } } void Application::setLowVelocityFilter(bool lowVelocityFilter) { From 0b0c3f0be4cee9aeea4a78f2239bd247082903fb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Dec 2016 16:43:50 -0800 Subject: [PATCH 056/118] cleanup some if-logic --- libraries/render-utils/src/Model.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 14c80faa96..436574a1ff 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -234,17 +234,19 @@ void Model::updateRenderItems() { render::PendingChanges pendingChanges; foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) { - if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } - // Ensure the model geometry was not reset between frames - if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) { - // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); + if (data._model && data._model->isLoaded()) { + if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + } + // Ensure the model geometry was not reset between frames + if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { + // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. + data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); - // update the model transform and bounding box for this render item. - const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices); + // update the model transform and bounding box for this render item. + const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); + data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices); + } } }); } From e6a20102d4653e17ad9b6383a31bc6253dc69007 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Dec 2016 11:31:34 -0800 Subject: [PATCH 057/118] debug window enhancements --- interface/src/Menu.cpp | 9 ++++++++ scripts/developer/debugging/debugWindow.js | 24 ++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b20343c439..4819220400 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -704,6 +704,15 @@ Menu::Menu() { addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L, qApp, SLOT(toggleLogDialog())); + action = addActionToQMenuAndActionHash(developerMenu, "Script Log (HMD friendly)..."); + connect(action, &QAction::triggered, [] { + auto scriptEngines = DependencyManager::get(); + QUrl defaultScriptsLoc = defaultScriptsLocation(); + defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js"); + scriptEngines->loadScript(defaultScriptsLoc.toString()); + }); + + // Developer > Stats addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats); diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index fb9f3f4847..30a050e667 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -8,6 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +(function() { // BEGIN LOCAL_SCOPE // Set up the qml ui var qml = Script.resolvePath('debugWindow.qml'); @@ -19,18 +20,33 @@ var window = new OverlayWindow({ window.setPosition(25, 50); window.closed.connect(function() { Script.stop(); }); +var getFormattedDate = function() { + var date = new Date(); + return date.getMonth() + "/" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds(); +}; + +var sendToLogWindow = function(type, message, scriptFileName) { + var typeFormatted = ""; + if (type) { + typeFormatted = type + " - "; + } + window.sendToQml("[" + getFormattedDate() + "] " + "[" + scriptFileName + "] " + typeFormatted + message); +}; + ScriptDiscoveryService.printedMessage.connect(function(message, scriptFileName) { - window.sendToQml("[" + scriptFileName + "] " + message); + sendToLogWindow("", message, scriptFileName); }); ScriptDiscoveryService.warningMessage.connect(function(message, scriptFileName) { - window.sendToQml("[" + scriptFileName + "] WARNING - " + message); + sendToLogWindow("WARNING", message, scriptFileName); }); ScriptDiscoveryService.errorMessage.connect(function(message, scriptFileName) { - window.sendToQml("[" + scriptFileName + "] ERROR - " + message); + sendToLogWindow("ERROR", message, scriptFileName); }); ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) { - window.sendToQml("[" + scriptFileName + "] INFO - " + message); + sendToLogWindow("INFO", message, scriptFileName); }); + +}()); // END LOCAL_SCOPE \ No newline at end of file From 8e7b062aa2b15b7c995f30259776fe2a1c993586 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Dec 2016 11:52:24 -0800 Subject: [PATCH 058/118] cleanup avatar data to include better documentation --- libraries/avatars/src/AvatarData.cpp | 115 +++++++++++++++------------ 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d77f70fbe9..f8805d3fc4 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -50,9 +50,20 @@ const glm::vec3 DEFAULT_LOCAL_AABOX_SCALE(1.0f); const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData"; namespace AvatarDataPacket { + // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. PACKED_BEGIN struct Header { + uint8_t packetStateFlags; // state flags, currently used to indicate if the packet is a minimal or fuller packet + } PACKED_END; + const size_t HEADER_SIZE = 1; + + PACKED_BEGIN struct MinimalAvatarInfo { + float globalPosition[3]; // avatar's position + } PACKED_END; + const size_t MINIMAL_AVATAR_INFO_SIZE = 12; + + PACKED_BEGIN struct AvatarInfo { float position[3]; // skeletal model's position float globalPosition[3]; // avatar's position float globalBoundingBoxCorner[3]; // global position of the lowest corner of the avatar's bounding box @@ -65,16 +76,16 @@ namespace AvatarDataPacket { float sensorToWorldTrans[3]; // fourth column of sensor to world matrix uint8_t flags; } PACKED_END; - const size_t HEADER_SIZE = 81; + const size_t AVATAR_INFO_SIZE = 81; - // only present if HAS_REFERENTIAL flag is set in header.flags + // only present if HAS_REFERENTIAL flag is set in AvatarInfo.flags PACKED_BEGIN struct ParentInfo { uint8_t parentUUID[16]; // rfc 4122 encoded uint16_t parentJointIndex; } PACKED_END; const size_t PARENT_INFO_SIZE = 18; - // only present if IS_FACESHIFT_CONNECTED flag is set in header.flags + // only present if IS_FACESHIFT_CONNECTED flag is set in AvatarInfo.flags PACKED_BEGIN struct FaceTrackerInfo { float leftEyeBlink; float rightEyeBlink; @@ -124,6 +135,8 @@ AvatarData::AvatarData() : setBodyRoll(0.0f); ASSERT(sizeof(AvatarDataPacket::Header) == AvatarDataPacket::HEADER_SIZE); + ASSERT(sizeof(AvatarDataPacket::MinimalAvatarInfo) == AvatarDataPacket::MINIMAL_AVATAR_INFO_SIZE); + ASSERT(sizeof(AvatarDataPacket::AvatarInfo) == AvatarDataPacket::AVATAR_INFO_SIZE); ASSERT(sizeof(AvatarDataPacket::ParentInfo) == AvatarDataPacket::PARENT_INFO_SIZE); ASSERT(sizeof(AvatarDataPacket::FaceTrackerInfo) == AvatarDataPacket::FACE_TRACKER_INFO_SIZE); } @@ -132,9 +145,9 @@ AvatarData::~AvatarData() { delete _headData; } -// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized. +// We cannot have a file-level variable (const or otherwise) in the AvatarInfo if it uses PathUtils, because that references Application, which will not yet initialized. // Thus we have a static class getter, referencing a static class var. -QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the header, every file would have it's own copy, even for class vars. +QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the AvatarInfo, every file would have it's own copy, even for class vars. const QUrl& AvatarData::defaultFullAvatarModelUrl() { if (_defaultFullAvatarModelUrl.isEmpty()) { _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst"); @@ -216,56 +229,56 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail) { memcpy(destinationBuffer, &_globalPosition, sizeof(_globalPosition)); destinationBuffer += sizeof(_globalPosition); } else { - auto header = reinterpret_cast(destinationBuffer); - header->position[0] = getLocalPosition().x; - header->position[1] = getLocalPosition().y; - header->position[2] = getLocalPosition().z; - header->globalPosition[0] = _globalPosition.x; - header->globalPosition[1] = _globalPosition.y; - header->globalPosition[2] = _globalPosition.z; - header->globalBoundingBoxCorner[0] = getPosition().x - _globalBoundingBoxCorner.x; - header->globalBoundingBoxCorner[1] = getPosition().y - _globalBoundingBoxCorner.y; - header->globalBoundingBoxCorner[2] = getPosition().z - _globalBoundingBoxCorner.z; + auto avatarInfo = reinterpret_cast(destinationBuffer); + avatarInfo->position[0] = getLocalPosition().x; + avatarInfo->position[1] = getLocalPosition().y; + avatarInfo->position[2] = getLocalPosition().z; + avatarInfo->globalPosition[0] = _globalPosition.x; + avatarInfo->globalPosition[1] = _globalPosition.y; + avatarInfo->globalPosition[2] = _globalPosition.z; + avatarInfo->globalBoundingBoxCorner[0] = getPosition().x - _globalBoundingBoxCorner.x; + avatarInfo->globalBoundingBoxCorner[1] = getPosition().y - _globalBoundingBoxCorner.y; + avatarInfo->globalBoundingBoxCorner[2] = getPosition().z - _globalBoundingBoxCorner.z; glm::vec3 bodyEulerAngles = glm::degrees(safeEulerAngles(getLocalOrientation())); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x); - packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z); - packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale()); - header->lookAtPosition[0] = _headData->_lookAtPosition.x; - header->lookAtPosition[1] = _headData->_lookAtPosition.y; - header->lookAtPosition[2] = _headData->_lookAtPosition.z; - header->audioLoudness = _headData->_audioLoudness; + packFloatAngleToTwoByte((uint8_t*)(avatarInfo->localOrientation + 0), bodyEulerAngles.y); + packFloatAngleToTwoByte((uint8_t*)(avatarInfo->localOrientation + 1), bodyEulerAngles.x); + packFloatAngleToTwoByte((uint8_t*)(avatarInfo->localOrientation + 2), bodyEulerAngles.z); + packFloatRatioToTwoByte((uint8_t*)(&avatarInfo->scale), getDomainLimitedScale()); + avatarInfo->lookAtPosition[0] = _headData->_lookAtPosition.x; + avatarInfo->lookAtPosition[1] = _headData->_lookAtPosition.y; + avatarInfo->lookAtPosition[2] = _headData->_lookAtPosition.z; + avatarInfo->audioLoudness = _headData->_audioLoudness; glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); - packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); + packOrientationQuatToSixBytes(avatarInfo->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); glm::vec3 scale = extractScale(sensorToWorldMatrix); - packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); - header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; - header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; - header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; + packFloatScalarToSignedTwoByteFixed((uint8_t*)&avatarInfo->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); + avatarInfo->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; + avatarInfo->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; + avatarInfo->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; - setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); + setSemiNibbleAt(avatarInfo->flags, KEY_STATE_START_BIT, _keyState); // hand state bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; - setSemiNibbleAt(header->flags, HAND_STATE_START_BIT, _handState & ~IS_FINGER_POINTING_FLAG); + setSemiNibbleAt(avatarInfo->flags, HAND_STATE_START_BIT, _handState & ~IS_FINGER_POINTING_FLAG); if (isFingerPointing) { - setAtBit(header->flags, HAND_STATE_FINGER_POINTING_BIT); + setAtBit(avatarInfo->flags, HAND_STATE_FINGER_POINTING_BIT); } // faceshift state if (_headData->_isFaceTrackerConnected) { - setAtBit(header->flags, IS_FACESHIFT_CONNECTED); + setAtBit(avatarInfo->flags, IS_FACESHIFT_CONNECTED); } // eye tracker state if (_headData->_isEyeTrackerConnected) { - setAtBit(header->flags, IS_EYE_TRACKER_CONNECTED); + setAtBit(avatarInfo->flags, IS_EYE_TRACKER_CONNECTED); } // referential state QUuid parentID = getParentID(); if (!parentID.isNull()) { - setAtBit(header->flags, HAS_REFERENTIAL); + setAtBit(avatarInfo->flags, HAS_REFERENTIAL); } - destinationBuffer += sizeof(AvatarDataPacket::Header); + destinationBuffer += sizeof(AvatarDataPacket::AvatarInfo); if (!parentID.isNull()) { auto parentInfo = reinterpret_cast(destinationBuffer); @@ -510,13 +523,13 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { quint64 now = usecTimestampNow(); - PACKET_READ_CHECK(Header, sizeof(AvatarDataPacket::Header)); - auto header = reinterpret_cast(sourceBuffer); - sourceBuffer += sizeof(AvatarDataPacket::Header); + PACKET_READ_CHECK(AvatarInfo, sizeof(AvatarDataPacket::AvatarInfo)); + auto avatarInfo = reinterpret_cast(sourceBuffer); + sourceBuffer += sizeof(AvatarDataPacket::AvatarInfo); - glm::vec3 position = glm::vec3(header->position[0], header->position[1], header->position[2]); - _globalPosition = glm::vec3(header->globalPosition[0], header->globalPosition[1], header->globalPosition[2]); - _globalBoundingBoxCorner = glm::vec3(header->globalBoundingBoxCorner[0], header->globalBoundingBoxCorner[1], header->globalBoundingBoxCorner[2]); + glm::vec3 position = glm::vec3(avatarInfo->position[0], avatarInfo->position[1], avatarInfo->position[2]); + _globalPosition = glm::vec3(avatarInfo->globalPosition[0], avatarInfo->globalPosition[1], avatarInfo->globalPosition[2]); + _globalBoundingBoxCorner = glm::vec3(avatarInfo->globalBoundingBoxCorner[0], avatarInfo->globalBoundingBoxCorner[1], avatarInfo->globalBoundingBoxCorner[2]); if (isNaN(position)) { if (shouldLogError(now)) { qCWarning(avatars) << "Discard AvatarData packet: position NaN, uuid " << getSessionUUID(); @@ -526,9 +539,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { setLocalPosition(position); float pitch, yaw, roll; - unpackFloatAngleFromTwoByte(header->localOrientation + 0, &yaw); - unpackFloatAngleFromTwoByte(header->localOrientation + 1, &pitch); - unpackFloatAngleFromTwoByte(header->localOrientation + 2, &roll); + unpackFloatAngleFromTwoByte(avatarInfo->localOrientation + 0, &yaw); + unpackFloatAngleFromTwoByte(avatarInfo->localOrientation + 1, &pitch); + unpackFloatAngleFromTwoByte(avatarInfo->localOrientation + 2, &roll); if (isNaN(yaw) || isNaN(pitch) || isNaN(roll)) { if (shouldLogError(now)) { qCWarning(avatars) << "Discard AvatarData packet: localOriention is NaN, uuid " << getSessionUUID(); @@ -545,7 +558,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } float scale; - unpackFloatRatioFromTwoByte((uint8_t*)&header->scale, scale); + unpackFloatRatioFromTwoByte((uint8_t*)&avatarInfo->scale, scale); if (isNaN(scale)) { if (shouldLogError(now)) { qCWarning(avatars) << "Discard AvatarData packet: scale NaN, uuid " << getSessionUUID(); @@ -554,7 +567,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } setTargetScale(scale); - glm::vec3 lookAt = glm::vec3(header->lookAtPosition[0], header->lookAtPosition[1], header->lookAtPosition[2]); + glm::vec3 lookAt = glm::vec3(avatarInfo->lookAtPosition[0], avatarInfo->lookAtPosition[1], avatarInfo->lookAtPosition[2]); if (isNaN(lookAt)) { if (shouldLogError(now)) { qCWarning(avatars) << "Discard AvatarData packet: lookAtPosition is NaN, uuid " << getSessionUUID(); @@ -563,7 +576,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } _headData->_lookAtPosition = lookAt; - float audioLoudness = header->audioLoudness; + float audioLoudness = avatarInfo->audioLoudness; if (isNaN(audioLoudness)) { if (shouldLogError(now)) { qCWarning(avatars) << "Discard AvatarData packet: audioLoudness is NaN, uuid " << getSessionUUID(); @@ -573,16 +586,16 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _headData->_audioLoudness = audioLoudness; glm::quat sensorToWorldQuat; - unpackOrientationQuatFromSixBytes(header->sensorToWorldQuat, sensorToWorldQuat); + unpackOrientationQuatFromSixBytes(avatarInfo->sensorToWorldQuat, sensorToWorldQuat); float sensorToWorldScale; - unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); - glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]); + unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&avatarInfo->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); + glm::vec3 sensorToWorldTrans(avatarInfo->sensorToWorldTrans[0], avatarInfo->sensorToWorldTrans[1], avatarInfo->sensorToWorldTrans[2]); glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); _sensorToWorldMatrixCache.set(sensorToWorldMatrix); { // bitFlags and face data - uint8_t bitItems = header->flags; + uint8_t bitItems = avatarInfo->flags; // key state, stored as a semi-nibble in the bitItems _keyState = (KeyState)getSemiNibbleAt(bitItems, KEY_STATE_START_BIT); From 59962286226c991c71282b071fb274a290dd53c0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 13:06:48 -0800 Subject: [PATCH 059/118] Starting off strong. --- interface/resources/qml/hifi/NameCard.qml | 16 +- interface/resources/qml/hifi/Pal.qml | 217 +++++++++++++--------- 2 files changed, 138 insertions(+), 95 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index ffb42d2fff..48f0f61d03 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -20,17 +20,27 @@ Column { property int displayTextHeight: 18; property int usernameTextHeight: 12; + RalewaySemiBold { + // Properties text: parent.displayName; - size: parent.displayTextHeight; elide: Text.ElideRight; + // Size width: parent.width; + // Text Size + size: parent.displayTextHeight; + // Text Positioning + verticalAlignment: Text.AlignVCenter; } RalewayLight { - visible: parent.displayName; + // Properties text: parent.userName; - size: parent.usernameTextHeight; elide: Text.ElideRight; + visible: parent.displayName; + // Size + size: parent.usernameTextHeight; width: parent.width; + // Text Positioning + verticalAlignment: Text.AlignVCenter; } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7a8dc4722e..060a14b1dd 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -31,13 +31,133 @@ import QtQuick.Controls 1.4 Rectangle { id: pal; - property int keepFromHorizontalScroll: 1; - width: parent.width - keepFromHorizontalScroll; + // Size + width: parent.width; height: parent.height; - - property int nameWidth: width/2; - property int actionWidth: nameWidth / (table.columnCount - 1); + // Properties + property int myCardHeight: 75; property int rowHeight: 50; + property int nameCardWidth: width*3/5; + property int separatorColWidth: 30; + property int actionWidth: (width - nameCardWidth - separatorColWidth) / (table.columnCount - 2); // "-2" for Name and Separator cols + + Column { + // This NameCard refers to the current user's NameCard (the one above the table) + NameCard { + id: myCard; + // Properties + displayName: myData.displayName; + userName: myData.userName; + // Size + width: nameCardWidth; + height: myCardHeight; + // Positioning + anchors.top: pal.top; + anchors.left: parent.left; + // Margins + anchors.leftMargin: 10; + } + // This TableView refers to the table (below the current user's NameCard) + TableView { + id: table; + // Size + height: pal.height - myCard.height; + width: pal.width; + // Positioning + anchors.top: myCard.bottom; + // Properties + frameVisible: false; + sortIndicatorVisible: true; + onSortIndicatorColumnChanged: sortModel(); + onSortIndicatorOrderChanged: sortModel(); + + TableViewColumn { + role: "displayName"; + title: "Name"; + width: nameCardWidth + } + TableViewColumn { + role: "ignore"; + title: "Ignore" + width: actionWidth + } + TableViewColumn { + title: ""; + width: separatorColWidth + } + TableViewColumn { + visible: iAmAdmin; + role: "mute"; + title: "Mute"; + width: actionWidth + } + TableViewColumn { + visible: iAmAdmin; + role: "kick"; + title: "Ban" + width: actionWidth + } + model: userModel; + + // This Rectangle refers to each Row in the table. + rowDelegate: Rectangle { // The only way I know to specify a row height. + height: rowHeight; + // The rest of this is cargo-culted to restore the default styling + SystemPalette { + id: myPalette; + colorGroup: SystemPalette.Active + } + color: { + var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base + return styleData.selected?myPalette.highlight:baseColor + } + } + + // This Item refers to the contents of each Cell + itemDelegate: Item { + id: itemCell; + property bool isCheckBox: typeof(styleData.value) === 'boolean'; + // This NameCard refers to the cell that contains an avatar's + // DisplayName and UserName + NameCard { + id: nameCard; + // Properties + displayName: styleData.value; + userName: model.userName; + visible: !isCheckBox; + // Size + width: nameCardWidth; + // Positioning + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + // Margins + anchors.leftMargin: 10; + } + // This Rectangle refers to the cells that contain the action buttons + Rectangle { + radius: itemCell.height / 4; + visible: isCheckBox; + color: styleData.value ? "green" : "red"; + anchors.fill: parent; + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + hoverEnabled: true; + onClicked: { + var newValue = !model[styleData.role]; + var datum = userData[model.userIndex]; + datum[styleData.role] = model[styleData.role] = newValue; + Users[styleData.role](model.sessionId); + // Just for now, while we cannot undo things: + userData.splice(model.userIndex, 1); + sortModel(); + } + } + } + } + } + } + property var userData: []; property var myData: ({displayName: "", userName: ""}); // valid dummy until set property bool iAmAdmin: false; @@ -135,91 +255,4 @@ Rectangle { target: table.selection onSelectionChanged: pal.noticeSelection() } - - Column { - NameCard { - id: myCard; - width: nameWidth; - displayName: myData.displayName; - userName: myData.userName; - } - TableView { - id: table; - TableViewColumn { - role: "displayName"; - title: "Name"; - width: nameWidth - } - TableViewColumn { - role: "ignore"; - title: "Ignore" - width: actionWidth - } - TableViewColumn { - title: ""; - width: actionWidth - } - TableViewColumn { - visible: iAmAdmin; - role: "mute"; - title: "Mute"; - width: actionWidth - } - TableViewColumn { - visible: iAmAdmin; - role: "kick"; - title: "Ban" - width: actionWidth - } - model: userModel; - rowDelegate: Rectangle { // The only way I know to specify a row height. - height: rowHeight; - // The rest of this is cargo-culted to restore the default styling - SystemPalette { - id: myPalette; - colorGroup: SystemPalette.Active - } - color: { - var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base - return styleData.selected?myPalette.highlight:baseColor - } - } - itemDelegate: Item { - id: itemCell; - property bool isCheckBox: typeof(styleData.value) === 'boolean'; - NameCard { - id: nameCard; - visible: !isCheckBox; - width: nameWidth; - displayName: styleData.value; - userName: model.userName; - } - Rectangle { - radius: itemCell.height / 4; - visible: isCheckBox; - color: styleData.value ? "green" : "red"; - anchors.fill: parent; - MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; - hoverEnabled: true; - onClicked: { - var newValue = !model[styleData.role]; - var datum = userData[model.userIndex]; - datum[styleData.role] = model[styleData.role] = newValue; - Users[styleData.role](model.sessionId); - // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1); - sortModel(); - } - } - } - } - height: pal.height - myCard.height; - width: pal.width; - sortIndicatorVisible: true; - onSortIndicatorColumnChanged: sortModel(); - onSortIndicatorOrderChanged: sortModel(); - } - } } From 46c787512ef4083ec307ec9a92768457e16e46f1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 14:05:55 -0800 Subject: [PATCH 060/118] Working --- interface/resources/qml/hifi/NameCard.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 195 +++++++++++----------- 2 files changed, 103 insertions(+), 96 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 48f0f61d03..ef3afa22ff 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -15,11 +15,11 @@ import "../styles-uit" Column { + // Properties property string displayName: ""; property string userName: ""; property int displayTextHeight: 18; - property int usernameTextHeight: 12; - + property int usernameTextHeight: 12 RalewaySemiBold { // Properties diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 060a14b1dd..b0710a1dda 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -41,7 +41,15 @@ Rectangle { property int separatorColWidth: 30; property int actionWidth: (width - nameCardWidth - separatorColWidth) / (table.columnCount - 2); // "-2" for Name and Separator cols - Column { + // This contains the current user's NameCard and will contain other information in the future + Item { + id: myInfo; + // Size + width: pal.width; + height: myCardHeight; + // Positioning + anchors.top: pal.top; + anchors.left: pal.left; // This NameCard refers to the current user's NameCard (the one above the table) NameCard { id: myCard; @@ -49,109 +57,108 @@ Rectangle { displayName: myData.displayName; userName: myData.userName; // Size - width: nameCardWidth; - height: myCardHeight; + width: nameCardWidth - anchors.leftMargin; // Positioning - anchors.top: pal.top; anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; // Margins anchors.leftMargin: 10; } - // This TableView refers to the table (below the current user's NameCard) - TableView { - id: table; - // Size - height: pal.height - myCard.height; - width: pal.width; - // Positioning - anchors.top: myCard.bottom; - // Properties - frameVisible: false; - sortIndicatorVisible: true; - onSortIndicatorColumnChanged: sortModel(); - onSortIndicatorOrderChanged: sortModel(); + } + // This TableView refers to the table (below the current user's NameCard) + TableView { + id: table; + // Size + height: pal.height - myInfo.height; + width: pal.width; + // Positioning + anchors.top: myInfo.bottom; + // Properties + frameVisible: false; + sortIndicatorVisible: true; + onSortIndicatorColumnChanged: sortModel(); + onSortIndicatorOrderChanged: sortModel(); - TableViewColumn { - role: "displayName"; - title: "Name"; - width: nameCardWidth - } - TableViewColumn { - role: "ignore"; - title: "Ignore" - width: actionWidth - } - TableViewColumn { - title: ""; - width: separatorColWidth - } - TableViewColumn { - visible: iAmAdmin; - role: "mute"; - title: "Mute"; - width: actionWidth - } - TableViewColumn { - visible: iAmAdmin; - role: "kick"; - title: "Ban" - width: actionWidth - } - model: userModel; + TableViewColumn { + role: "displayName"; + title: "Name"; + width: nameCardWidth + } + TableViewColumn { + role: "ignore"; + title: "Ignore" + width: actionWidth + } + TableViewColumn { + title: ""; + width: separatorColWidth + } + TableViewColumn { + visible: iAmAdmin; + role: "mute"; + title: "Mute"; + width: actionWidth + } + TableViewColumn { + visible: iAmAdmin; + role: "kick"; + title: "Ban" + width: actionWidth + } + model: userModel; - // This Rectangle refers to each Row in the table. - rowDelegate: Rectangle { // The only way I know to specify a row height. - height: rowHeight; - // The rest of this is cargo-culted to restore the default styling - SystemPalette { - id: myPalette; - colorGroup: SystemPalette.Active - } - color: { - var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base - return styleData.selected?myPalette.highlight:baseColor - } + // This Rectangle refers to each Row in the table. + rowDelegate: Rectangle { // The only way I know to specify a row height. + height: rowHeight; + // The rest of this is cargo-culted to restore the default styling + SystemPalette { + id: myPalette; + colorGroup: SystemPalette.Active } + color: { + var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base + return styleData.selected?myPalette.highlight:baseColor + } + } - // This Item refers to the contents of each Cell - itemDelegate: Item { - id: itemCell; - property bool isCheckBox: typeof(styleData.value) === 'boolean'; - // This NameCard refers to the cell that contains an avatar's - // DisplayName and UserName - NameCard { - id: nameCard; - // Properties - displayName: styleData.value; - userName: model.userName; - visible: !isCheckBox; - // Size - width: nameCardWidth; - // Positioning - anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; - // Margins - anchors.leftMargin: 10; - } - // This Rectangle refers to the cells that contain the action buttons - Rectangle { - radius: itemCell.height / 4; - visible: isCheckBox; - color: styleData.value ? "green" : "red"; + // This Item refers to the contents of each Cell + itemDelegate: Item { + id: itemCell; + property bool isCheckBox: typeof(styleData.value) === 'boolean'; + // This NameCard refers to the cell that contains an avatar's + // DisplayName and UserName + NameCard { + id: nameCard; + // Properties + displayName: styleData.value; + userName: model.userName; + visible: !isCheckBox; + // Size + width: nameCardWidth - anchors.leftMargin; + // Positioning + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + // Margins + anchors.leftMargin: 10; + } + // This Rectangle refers to the cells that contain the action buttons + Rectangle { + radius: itemCell.height / 4; + visible: isCheckBox; + color: styleData.value ? "green" : "red"; + anchors.fill: parent; + MouseArea { anchors.fill: parent; - MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; - hoverEnabled: true; - onClicked: { - var newValue = !model[styleData.role]; - var datum = userData[model.userIndex]; - datum[styleData.role] = model[styleData.role] = newValue; - Users[styleData.role](model.sessionId); - // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1); - sortModel(); - } + acceptedButtons: Qt.LeftButton; + hoverEnabled: true; + onClicked: { + var newValue = !model[styleData.role]; + var datum = userData[model.userIndex]; + datum[styleData.role] = model[styleData.role] = newValue; + Users[styleData.role](model.sessionId); + // Just for now, while we cannot undo things: + userData.splice(model.userIndex, 1); + sortModel(); } } } From deb40f67df69098297832054ced6180a220cfbfe Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 14:26:49 -0800 Subject: [PATCH 061/118] Steady progress --- interface/resources/qml/hifi/NameCard.qml | 65 ++++++++++++++++------- interface/resources/qml/hifi/Pal.qml | 10 ++-- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index ef3afa22ff..e65ffa3561 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -13,34 +13,59 @@ import Hifi 1.0 import QtQuick 2.5 import "../styles-uit" +Row { + id: thisNameCard; + // Spacing + spacing: 5; + // Margins + anchors.leftMargin: 5; -Column { // Properties property string displayName: ""; property string userName: ""; property int displayTextHeight: 18; - property int usernameTextHeight: 12 + property int usernameTextHeight: 12; - RalewaySemiBold { - // Properties - text: parent.displayName; - elide: Text.ElideRight; + Column { + id: avatarImage; // Size - width: parent.width; - // Text Size - size: parent.displayTextHeight; - // Text Positioning - verticalAlignment: Text.AlignVCenter; + width: parent.height - 2; + height: width; + Rectangle { + anchors.fill: parent; + radius: parent.width*0.5; + color: "#AAA5AD"; + } } - RalewayLight { - // Properties - text: parent.userName; - elide: Text.ElideRight; - visible: parent.displayName; + Column { + id: textContainer; // Size - size: parent.usernameTextHeight; - width: parent.width; - // Text Positioning - verticalAlignment: Text.AlignVCenter; + width: parent.width - avatarImage.width; + + RalewaySemiBold { + id: displayNameText; + // Properties + text: thisNameCard.displayName; + elide: Text.ElideRight; + // Size + width: parent.width; + // Text Size + size: thisNameCard.displayTextHeight; + // Text Positioning + verticalAlignment: Text.AlignVCenter; + } + RalewayLight { + id: userNameText; + // Properties + text: thisNameCard.userName; + elide: Text.ElideRight; + visible: thisNameCard.displayName; + // Size + width: parent.width; + // Text Size + size: thisNameCard.usernameTextHeight; + // Text Positioning + verticalAlignment: Text.AlignVCenter; + } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index b0710a1dda..0f52c3686a 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -58,11 +58,10 @@ Rectangle { userName: myData.userName; // Size width: nameCardWidth - anchors.leftMargin; + height: myCardHeight; // Positioning anchors.left: parent.left; anchors.verticalCenter: parent.verticalCenter; - // Margins - anchors.leftMargin: 10; } } // This TableView refers to the table (below the current user's NameCard) @@ -125,6 +124,7 @@ Rectangle { itemDelegate: Item { id: itemCell; property bool isCheckBox: typeof(styleData.value) === 'boolean'; + property bool isSeparator: styleData.value === ''; // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { @@ -132,19 +132,17 @@ Rectangle { // Properties displayName: styleData.value; userName: model.userName; - visible: !isCheckBox; + visible: !isCheckBox && !isSeparator; // Size width: nameCardWidth - anchors.leftMargin; // Positioning anchors.left: parent.left; anchors.verticalCenter: parent.verticalCenter; - // Margins - anchors.leftMargin: 10; } // This Rectangle refers to the cells that contain the action buttons Rectangle { radius: itemCell.height / 4; - visible: isCheckBox; + visible: isCheckBox && !isSeparator; color: styleData.value ? "green" : "red"; anchors.fill: parent; MouseArea { From e7c2229b2c52b2d859d09be4cd7465524c9dcf61 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 15:31:43 -0800 Subject: [PATCH 062/118] More progress --- interface/resources/qml/hifi/NameCard.qml | 89 +++++++++++++++++++++-- interface/resources/qml/hifi/Pal.qml | 31 ++++---- scripts/system/pal.js | 2 +- 3 files changed, 99 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index e65ffa3561..0856a5f183 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -9,16 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import Hifi 1.0 +import Hifi 1.0 as Hifi import QtQuick 2.5 import "../styles-uit" Row { id: thisNameCard; // Spacing - spacing: 5; - // Margins - anchors.leftMargin: 5; + spacing: 10; + // Anchors + //anchors.topMargin: 10; + anchors.leftMargin: 10; + //anchors.verticalCenter: parent.verticalCenter // Properties property string displayName: ""; @@ -29,19 +31,24 @@ Row { Column { id: avatarImage; // Size - width: parent.height - 2; + width: 50; height: width; Rectangle { - anchors.fill: parent; radius: parent.width*0.5; color: "#AAA5AD"; + // Anchors + width: parent.width + height: parent.height; } } Column { id: textContainer; // Size - width: parent.width - avatarImage.width; + width: parent.width - avatarImage.width - parent.anchors.leftMargin*2 - parent.spacing; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + // DisplayName Text RalewaySemiBold { id: displayNameText; // Properties @@ -54,6 +61,8 @@ Row { // Text Positioning verticalAlignment: Text.AlignVCenter; } + + // UserName Text RalewayLight { id: userNameText; // Properties @@ -67,5 +76,71 @@ Row { // Text Positioning verticalAlignment: Text.AlignVCenter; } + + // Spacer + Item { + height: 7; + width: parent.width; + } + + // VU Meter + Hifi.AvatarInputs { + id: nameCardVUMeter; + objectName: "AvatarInputs"; + width: parent.width; + height: 30; + // Avatar Audio VU Meter + Item { + id: controls; + width: nameCardVUMeter.width; + + Rectangle { + anchors.fill: parent; + color: nameCardVUMeter.audioClipping ? "red" : "#696969"; + + Item { + id: audioMeter + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: nameCardVUMeter.iconPadding + anchors.right: parent.right + anchors.rightMargin: nameCardVUMeter.iconPadding + height: 8 + Rectangle { + id: blueRect + color: "blue" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: parent.width / 4 + } + Rectangle { + id: greenRect + color: "green" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: blueRect.right + anchors.right: redRect.left + } + Rectangle { + id: redRect + color: "red" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: parent.width / 5 + } + Rectangle { + z: 100 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: (1.0 - nameCardVUMeter.audioLevel) * parent.width + color: "#dddddd"; + } + } + } + } + } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 0f52c3686a..502ff16971 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -36,10 +36,10 @@ Rectangle { height: parent.height; // Properties property int myCardHeight: 75; - property int rowHeight: 50; - property int nameCardWidth: width*3/5; + property int rowHeight: 65; property int separatorColWidth: 30; - property int actionWidth: (width - nameCardWidth - separatorColWidth) / (table.columnCount - 2); // "-2" for Name and Separator cols + property int actionButtonWidth: 50; + property int nameCardWidth: width - separatorColWidth- actionButtonWidth*(table.columnCount - 2); // "-2" for Name and Separator cols; // This contains the current user's NameCard and will contain other information in the future Item { @@ -47,9 +47,11 @@ Rectangle { // Size width: pal.width; height: myCardHeight; - // Positioning + // Anchors anchors.top: pal.top; anchors.left: pal.left; + anchors.topMargin: 10; + anchors.bottomMargin: anchors.topMargin; // This NameCard refers to the current user's NameCard (the one above the table) NameCard { id: myCard; @@ -57,11 +59,10 @@ Rectangle { displayName: myData.displayName; userName: myData.userName; // Size - width: nameCardWidth - anchors.leftMargin; - height: myCardHeight; - // Positioning + width: nameCardWidth; + height: parent.height; + // Anchors anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; } } // This TableView refers to the table (below the current user's NameCard) @@ -70,7 +71,7 @@ Rectangle { // Size height: pal.height - myInfo.height; width: pal.width; - // Positioning + // Anchors anchors.top: myInfo.bottom; // Properties frameVisible: false; @@ -86,7 +87,7 @@ Rectangle { TableViewColumn { role: "ignore"; title: "Ignore" - width: actionWidth + width: actionButtonWidth } TableViewColumn { title: ""; @@ -96,13 +97,13 @@ Rectangle { visible: iAmAdmin; role: "mute"; title: "Mute"; - width: actionWidth + width: actionButtonWidth } TableViewColumn { visible: iAmAdmin; role: "kick"; title: "Ban" - width: actionWidth + width: actionButtonWidth } model: userModel; @@ -134,10 +135,10 @@ Rectangle { userName: model.userName; visible: !isCheckBox && !isSeparator; // Size - width: nameCardWidth - anchors.leftMargin; - // Positioning + width: nameCardWidth; + height: parent.height; + // Anchors anchors.left: parent.left; - anchors.verticalCenter: parent.verticalCenter; } // This Rectangle refers to the cells that contain the action buttons Rectangle { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index c426f3fd87..b66d9afa67 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -142,7 +142,7 @@ function usernameFromIDReply(id, username, machineFingerprint) { // If the ID we've received is our ID... if (AvatarList.getAvatar('').sessionUUID === id) { // Set the data to contain specific strings. - data = ['', username + ' (hidden)'] + data = ['', username] } else { // Set the data to contain the ID and the username+ID concat string. data = [id, username + '/' + machineFingerprint]; From feafc1683333f279ccc7b77920db32c6eae85f73 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 15:41:28 -0800 Subject: [PATCH 063/118] Frequent checkpoints are good. --- interface/resources/qml/hifi/NameCard.qml | 4 ++-- interface/resources/qml/hifi/Pal.qml | 4 ++++ scripts/system/pal.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0856a5f183..a600d1b59f 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -18,9 +18,9 @@ Row { // Spacing spacing: 10; // Anchors - //anchors.topMargin: 10; + anchors.topMargin: 10; anchors.leftMargin: 10; - //anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: parent.verticalCenter; // Properties property string displayName: ""; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 502ff16971..1c7fa067f6 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -109,6 +109,7 @@ Rectangle { // This Rectangle refers to each Row in the table. rowDelegate: Rectangle { // The only way I know to specify a row height. + // Size height: rowHeight; // The rest of this is cargo-culted to restore the default styling SystemPalette { @@ -126,6 +127,9 @@ Rectangle { id: itemCell; property bool isCheckBox: typeof(styleData.value) === 'boolean'; property bool isSeparator: styleData.value === ''; + // Anchors + anchors.topMargin: 10; + anchors.bottomMargin: anchors.topMargin; // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index b66d9afa67..e5d28e4174 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -145,7 +145,7 @@ function usernameFromIDReply(id, username, machineFingerprint) { data = ['', username] } else { // Set the data to contain the ID and the username+ID concat string. - data = [id, username + '/' + machineFingerprint]; + data = [id, username || machineFingerprint]; } print('Username Data:', JSON.stringify(data)); // Ship the data off to QML From 7999e14ecfe0d3b3a480cdd612659a7423bccf1d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 19 Dec 2016 17:12:53 -0800 Subject: [PATCH 064/118] Kinda screwed things up by changing styles --- interface/resources/qml/hifi/NameCard.qml | 28 +++++++------ interface/resources/qml/hifi/Pal.qml | 49 ++++++++++++++--------- scripts/system/pal.js | 5 ++- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index a600d1b59f..3183f8b6ce 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -18,11 +18,16 @@ Row { // Spacing spacing: 10; // Anchors - anchors.topMargin: 10; - anchors.leftMargin: 10; - anchors.verticalCenter: parent.verticalCenter; + anchors.top: parent.top; + anchors { + topMargin: (parent.height - contentHeight)/2; + bottomMargin: (parent.height - contentHeight)/2; + leftMargin: 10; + rightMargin: 10; + } // Properties + property int contentHeight: 50; property string displayName: ""; property string userName: ""; property int displayTextHeight: 18; @@ -31,8 +36,8 @@ Row { Column { id: avatarImage; // Size - width: 50; - height: width; + height: contentHeight; + width: height; Rectangle { radius: parent.width*0.5; color: "#AAA5AD"; @@ -44,12 +49,11 @@ Row { Column { id: textContainer; // Size - width: parent.width - avatarImage.width - parent.anchors.leftMargin*2 - parent.spacing; - // Anchors - anchors.verticalCenter: parent.verticalCenter; + width: parent.width - avatarImage.width - parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing; + height: contentHeight; // DisplayName Text - RalewaySemiBold { + FiraSansSemiBold { id: displayNameText; // Properties text: thisNameCard.displayName; @@ -63,7 +67,7 @@ Row { } // UserName Text - RalewayLight { + FiraSansSemiBold { id: userNameText; // Properties text: thisNameCard.userName; @@ -84,11 +88,11 @@ Row { } // VU Meter - Hifi.AvatarInputs { + Rectangle { id: nameCardVUMeter; objectName: "AvatarInputs"; width: parent.width; - height: 30; + height: 4; // Avatar Audio VU Meter Item { id: controls; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1c7fa067f6..a977a77f64 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -28,6 +28,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import "../styles-uit" +import "../controls-uit" as HifiControls Rectangle { id: pal; @@ -35,10 +37,10 @@ Rectangle { width: parent.width; height: parent.height; // Properties - property int myCardHeight: 75; - property int rowHeight: 65; - property int separatorColWidth: 30; - property int actionButtonWidth: 50; + property int myCardHeight: 70; + property int rowHeight: 70; + property int separatorColWidth: 10; + property int actionButtonWidth: 55; property int nameCardWidth: width - separatorColWidth- actionButtonWidth*(table.columnCount - 2); // "-2" for Name and Separator cols; // This contains the current user's NameCard and will contain other information in the future @@ -50,8 +52,6 @@ Rectangle { // Anchors anchors.top: pal.top; anchors.left: pal.left; - anchors.topMargin: 10; - anchors.bottomMargin: anchors.topMargin; // This NameCard refers to the current user's NameCard (the one above the table) NameCard { id: myCard; @@ -66,7 +66,7 @@ Rectangle { } } // This TableView refers to the table (below the current user's NameCard) - TableView { + HifiControls.Table { id: table; // Size height: pal.height - myInfo.height; @@ -81,29 +81,41 @@ Rectangle { TableViewColumn { role: "displayName"; - title: "Name"; - width: nameCardWidth + title: "NAMES"; + width: nameCardWidth; + movable: false; + } + TableViewColumn { + role: "personalMute"; + title: "MUTE" + width: actionButtonWidth; + movable: false; } TableViewColumn { role: "ignore"; - title: "Ignore" - width: actionButtonWidth + title: "IGNORE"; + width: actionButtonWidth; + movable: false; } TableViewColumn { title: ""; - width: separatorColWidth + width: separatorColWidth; + resizable: false; + movable: false; } TableViewColumn { visible: iAmAdmin; role: "mute"; - title: "Mute"; - width: actionButtonWidth + title: "SILENCE"; + width: actionButtonWidth; + movable: false; } TableViewColumn { visible: iAmAdmin; role: "kick"; - title: "Ban" - width: actionButtonWidth + title: "BAN" + width: actionButtonWidth; + movable: false; } model: userModel; @@ -127,9 +139,6 @@ Rectangle { id: itemCell; property bool isCheckBox: typeof(styleData.value) === 'boolean'; property bool isSeparator: styleData.value === ''; - // Anchors - anchors.topMargin: 10; - anchors.bottomMargin: anchors.topMargin; // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { @@ -248,7 +257,7 @@ Rectangle { datum[property] = false; } } - ['ignore', 'spacer', 'mute', 'kick'].forEach(init); + ['personalMute', 'ignore', 'spacer', 'mute', 'kick'].forEach(init); datum.userIndex = userIndex++; userModel.append(datum); }); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index e5d28e4174..916556fdd7 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -79,7 +79,7 @@ ExtendedOverlay.applyPickRay = function (pickRay, cb) { // cb(overlay) on the on var pal = new OverlayWindow({ title: 'People Action List', source: 'hifi/Pal.qml', - width: 480, + width: 580, height: 640, visible: false }); @@ -144,7 +144,8 @@ function usernameFromIDReply(id, username, machineFingerprint) { // Set the data to contain specific strings. data = ['', username] } else { - // Set the data to contain the ID and the username+ID concat string. + // Set the data to contain the ID and the username (if we have one) + // or fingerprint (if we don't have a username) string. data = [id, username || machineFingerprint]; } print('Username Data:', JSON.stringify(data)); From db2da8f06b8c4a581c00a2fa01df954f9838fd34 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Dec 2016 11:37:10 -0800 Subject: [PATCH 065/118] Great cleanup --- .../resources/qml/controls-uit/Table.qml | 4 +++ interface/resources/qml/hifi/Pal.qml | 33 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 35029ad8bf..865e24945e 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -20,6 +20,7 @@ TableView { property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light property bool expandSelectedRow: false + property bool centerHeaderText: false model: ListModel { } @@ -34,9 +35,12 @@ TableView { size: hifi.fontSizes.tableHeading font.capitalization: Font.AllUppercase color: hifi.colors.baseGrayHighlight + horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) anchors { left: parent.left leftMargin: hifi.dimensions.tablePadding + right: parent.right + rightMargin: hifi.dimensions.tablePadding verticalCenter: parent.verticalCenter } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index a977a77f64..7a48619550 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -31,7 +31,7 @@ import QtQuick.Controls 1.4 import "../styles-uit" import "../controls-uit" as HifiControls -Rectangle { +Item { id: pal; // Size width: parent.width; @@ -40,18 +40,19 @@ Rectangle { property int myCardHeight: 70; property int rowHeight: 70; property int separatorColWidth: 10; - property int actionButtonWidth: 55; - property int nameCardWidth: width - separatorColWidth- actionButtonWidth*(table.columnCount - 2); // "-2" for Name and Separator cols; + property int actionButtonWidth: 70; + property int nameCardWidth: width - (iAmAdmin ? separatorColWidth : 0) - actionButtonWidth*(iAmAdmin ? 4 : 2); // This contains the current user's NameCard and will contain other information in the future - Item { + Rectangle { id: myInfo; // Size width: pal.width; height: myCardHeight; // Anchors anchors.top: pal.top; - anchors.left: pal.left; + // Properties + radius: hifi.dimensions.borderRadius; // This NameCard refers to the current user's NameCard (the one above the table) NameCard { id: myCard; @@ -65,17 +66,34 @@ Rectangle { anchors.left: parent.left; } } + // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle + Rectangle { + color: "#FFFFFF"; + width: pal.width; + height: 10; + anchors.top: myInfo.bottom; + anchors.left: parent.left; + } + Rectangle { + color: "#FFFFFF"; + width: pal.width; + height: 10; + anchors.bottom: table.top; + anchors.left: parent.left; + } // This TableView refers to the table (below the current user's NameCard) HifiControls.Table { id: table; // Size height: pal.height - myInfo.height; - width: pal.width; + width: pal.width - 4; // Anchors + anchors.left: parent.left; anchors.top: myInfo.bottom; // Properties - frameVisible: false; + centerHeaderText: true; sortIndicatorVisible: true; + headerVisible: true; onSortIndicatorColumnChanged: sortModel(); onSortIndicatorOrderChanged: sortModel(); @@ -98,6 +116,7 @@ Rectangle { movable: false; } TableViewColumn { + visible: iAmAdmin; title: ""; width: separatorColWidth; resizable: false; From 5b77202c52ffd24e251a214ea04c8422bfe588f3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Dec 2016 13:23:49 -0800 Subject: [PATCH 066/118] Popups wow! --- .../resources/icons/defaultNameCardUser.png | Bin 0 -> 50400 bytes interface/resources/qml/hifi/NameCard.qml | 8 +-- interface/resources/qml/hifi/Pal.qml | 55 +++++++++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 interface/resources/icons/defaultNameCardUser.png diff --git a/interface/resources/icons/defaultNameCardUser.png b/interface/resources/icons/defaultNameCardUser.png new file mode 100644 index 0000000000000000000000000000000000000000..807ca2fa87840b94778e027321d68cfd0288108f GIT binary patch literal 50400 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 literal 0 HcmV?d00001 diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 3183f8b6ce..052b17f99d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -38,11 +38,11 @@ Row { // Size height: contentHeight; width: height; - Rectangle { - radius: parent.width*0.5; - color: "#AAA5AD"; + Image { + id: userImage; + source: "../../icons/defaultNameCardUser.png" // Anchors - width: parent.width + width: parent.width; height: parent.height; } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7a48619550..81c036034b 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -85,7 +85,7 @@ Item { HifiControls.Table { id: table; // Size - height: pal.height - myInfo.height; + height: pal.height - myInfo.height - 4; width: pal.width - 4; // Anchors anchors.left: parent.left; @@ -195,6 +195,59 @@ Item { } } } + // This Rectangle refers to the [?] popup button + Rectangle { + color: hifi.colors.tableBackgroundLight; + width: 18; + height: hifi.dimensions.tableHeaderHeight - 2; + anchors.left: table.left; + anchors.top: table.top; + anchors.topMargin: 1; + anchors.leftMargin: nameCardWidth/2 + 23; + RalewayRegular { + id: helpText; + text: "[?]"; + size: hifi.fontSizes.tableHeading; + font.capitalization: Font.AllUppercase; + color: hifi.colors.darkGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + anchors.fill: parent; + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + hoverEnabled: true; + onClicked: namesPopup.visible = true; + onEntered: helpText.color = hifi.colors.baseGrayHighlight; + onExited: helpText.color = hifi.colors.darkGray; + } + } + // Explanitory popup upon clicking "[?]" + Item { + visible: false; + id: namesPopup; + anchors.fill: pal; + Rectangle { + anchors.fill: parent; + color: "black"; + opacity: 0.5; + radius: hifi.dimensions.borderRadius; + } + Rectangle { + width: 400; + height: 200; + anchors.centerIn: parent; + color: "black"; + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: { + namesPopup.visible = false; + } + } + } property var userData: []; property var myData: ({displayName: "", userName: ""}); // valid dummy until set From 9b65a72bb58339b9586902ff09d0bc2be7b0bdbe Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Dec 2016 13:53:26 -0800 Subject: [PATCH 067/118] Popup essentially done --- interface/resources/qml/hifi/Pal.qml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 81c036034b..0bcff94739 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -235,10 +235,20 @@ Item { radius: hifi.dimensions.borderRadius; } Rectangle { - width: 400; - height: 200; + width: Math.min(parent.width * 0.75, 400); + height: popupText.contentHeight*2; anchors.centerIn: parent; - color: "black"; + radius: hifi.dimensions.borderRadius; + color: "white"; + FiraSansSemiBold { + id: popupText; + text: "This is temporary text. It will eventually be used to explain what 'Names' means."; + size: hifi.fontSizes.textFieldInput; + color: hifi.colors.darkGray; + horizontalAlignment: Text.AlignHCenter; + anchors.fill: parent; + wrapMode: Text.WordWrap; + } } MouseArea { anchors.fill: parent; From 0be4db1e68ba2fecd8dc090aa32c5f77b1f0dc05 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Dec 2016 14:28:49 -0800 Subject: [PATCH 068/118] Checkboxes! --- .../resources/qml/controls-uit/CheckBox.qml | 2 +- interface/resources/qml/hifi/NameCard.qml | 2 +- interface/resources/qml/hifi/Pal.qml | 32 ++++++++----------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 544609b478..a3050df22a 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -21,7 +21,7 @@ Original.CheckBox { property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light - readonly property int boxSize: 14 + property int boxSize: 14 readonly property int boxRadius: 3 readonly property int checkSize: 10 readonly property int checkRadius: 2 diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 052b17f99d..56fa0521ae 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -67,7 +67,7 @@ Row { } // UserName Text - FiraSansSemiBold { + FiraSansRegular { id: userNameText; // Properties text: thisNameCard.userName; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 0bcff94739..3a7c3d45b7 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -172,25 +172,21 @@ Item { // Anchors anchors.left: parent.left; } - // This Rectangle refers to the cells that contain the action buttons - Rectangle { - radius: itemCell.height / 4; + + // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) + HifiControls.CheckBox { visible: isCheckBox && !isSeparator; - color: styleData.value ? "green" : "red"; - anchors.fill: parent; - MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; - hoverEnabled: true; - onClicked: { - var newValue = !model[styleData.role]; - var datum = userData[model.userIndex]; - datum[styleData.role] = model[styleData.role] = newValue; - Users[styleData.role](model.sessionId); - // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1); - sortModel(); - } + anchors.centerIn: parent; + colorScheme: hifi.colorSchemes.dark; + boxSize: 22; + onClicked: { + var newValue = !model[styleData.role]; + var datum = userData[model.userIndex]; + datum[styleData.role] = model[styleData.role] = newValue; + Users[styleData.role](model.sessionId); + // Just for now, while we cannot undo things: + userData.splice(model.userIndex, 1); + sortModel(); } } } From 630fb8696f8c32da8138c4a1731e53ca0e2aeb22 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 20 Dec 2016 17:18:28 -0800 Subject: [PATCH 069/118] Lots of progress today --- .../resources/qml/controls-uit/CheckBox.qml | 22 ++- interface/resources/qml/hifi/NameCard.qml | 168 ++++++++---------- interface/resources/qml/hifi/Pal.qml | 13 +- interface/src/ui/AvatarInputs.cpp | 1 + scripts/system/pal.js | 2 +- 5 files changed, 100 insertions(+), 106 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index a3050df22a..aec579755a 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -23,7 +23,7 @@ Original.CheckBox { property int boxSize: 14 readonly property int boxRadius: 3 - readonly property int checkSize: 10 + readonly property int checkSize: Math.max(boxSize - 8, 10) readonly property int checkRadius: 2 style: CheckBoxStyle { @@ -32,21 +32,35 @@ Original.CheckBox { width: boxSize height: boxSize radius: boxRadius + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) gradient: Gradient { GradientStop { position: 0.2 color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) } GradientStop { position: 1.0 color: pressed || hovered - ? (checkBox.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) } } + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + Rectangle { id: check width: checkSize @@ -54,7 +68,7 @@ Original.CheckBox { radius: checkRadius anchors.centerIn: parent color: hifi.colors.checkboxChecked - border.width: 1 + border.width: 2 border.color: hifi.colors.checkboxCheckedBorder visible: checked && !pressed || !checked && pressed } diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 56fa0521ae..4304c407c0 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -11,138 +11,124 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 +import QtGraphicalEffects 1.0 import "../styles-uit" Row { - id: thisNameCard; + id: thisNameCard // Spacing - spacing: 10; + spacing: 10 // Anchors - anchors.top: parent.top; + anchors.top: parent.top anchors { - topMargin: (parent.height - contentHeight)/2; - bottomMargin: (parent.height - contentHeight)/2; - leftMargin: 10; - rightMargin: 10; + topMargin: (parent.height - contentHeight)/2 + bottomMargin: (parent.height - contentHeight)/2 + leftMargin: 10 + rightMargin: 10 } // Properties - property int contentHeight: 50; - property string displayName: ""; - property string userName: ""; - property int displayTextHeight: 18; - property int usernameTextHeight: 12; + property int contentHeight: 50 + property string displayName: "" + property string userName: "" + property int displayTextHeight: 18 + property int usernameTextHeight: 12 Column { - id: avatarImage; + id: avatarImage // Size - height: contentHeight; - width: height; + height: contentHeight + width: height Image { - id: userImage; + id: userImage source: "../../icons/defaultNameCardUser.png" // Anchors - width: parent.width; - height: parent.height; + width: parent.width + height: parent.height } } Column { - id: textContainer; + id: textContainer // Size - width: parent.width - avatarImage.width - parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing; - height: contentHeight; + width: parent.width - avatarImage.width - parent.anchors.leftMargin - parent.anchors.rightMargin - parent.spacing + height: contentHeight // DisplayName Text FiraSansSemiBold { - id: displayNameText; + id: displayNameText // Properties - text: thisNameCard.displayName; - elide: Text.ElideRight; + text: thisNameCard.displayName + elide: Text.ElideRight // Size - width: parent.width; + width: parent.width // Text Size - size: thisNameCard.displayTextHeight; + size: thisNameCard.displayTextHeight // Text Positioning - verticalAlignment: Text.AlignVCenter; + verticalAlignment: Text.AlignVCenter } // UserName Text FiraSansRegular { - id: userNameText; + id: userNameText // Properties - text: thisNameCard.userName; - elide: Text.ElideRight; - visible: thisNameCard.displayName; + text: thisNameCard.userName + elide: Text.ElideRight + visible: thisNameCard.displayName // Size - width: parent.width; + width: parent.width // Text Size - size: thisNameCard.usernameTextHeight; + size: thisNameCard.usernameTextHeight // Text Positioning - verticalAlignment: Text.AlignVCenter; + verticalAlignment: Text.AlignVCenter } // Spacer Item { - height: 7; - width: parent.width; + height: 4 + width: parent.width } // VU Meter - Rectangle { - id: nameCardVUMeter; - objectName: "AvatarInputs"; - width: parent.width; - height: 4; - // Avatar Audio VU Meter - Item { - id: controls; - width: nameCardVUMeter.width; - - Rectangle { - anchors.fill: parent; - color: nameCardVUMeter.audioClipping ? "red" : "#696969"; - - Item { - id: audioMeter - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: nameCardVUMeter.iconPadding - anchors.right: parent.right - anchors.rightMargin: nameCardVUMeter.iconPadding - height: 8 - Rectangle { - id: blueRect - color: "blue" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - width: parent.width / 4 - } - Rectangle { - id: greenRect - color: "green" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: blueRect.right - anchors.right: redRect.left - } - Rectangle { - id: redRect - color: "red" - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - width: parent.width / 5 - } - Rectangle { - z: 100 - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - width: (1.0 - nameCardVUMeter.audioLevel) * parent.width - color: "#dddddd"; - } - } + Rectangle { // CHANGEME to the appropriate type! + id: nameCardVUMeter + objectName: "AvatarInputs" + // Size + width: parent.width + height: 8 + // Style + radius: 4 + // Rectangle for the VU meter base + Rectangle { + id: vuMeterBase + // Anchors + anchors.fill: parent + // Style + color: "#eeeeee" + radius: parent.radius + } + // Rectangle for the VU meter audio level + Rectangle { + id: vuMeterLevel + // Size + width: (nameCardVUMeter.audioLevel) * parent.width + // Style + color: "#E3E3E3" + radius: parent.radius + // Anchors + anchors.bottom: parent.bottom + anchors.top: parent.top + anchors.left: parent.left + } + // Gradient for the VU meter audio level + LinearGradient { + anchors.fill: vuMeterLevel + source: vuMeterLevel + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient { + GradientStop { position: 0.05; color: "#00CFEF" } + GradientStop { position: 0.5; color: "#9450A5" } + GradientStop { position: 0.95; color: "#EA4C5F" } } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 3a7c3d45b7..b62c721ead 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -142,15 +142,9 @@ Item { rowDelegate: Rectangle { // The only way I know to specify a row height. // Size height: rowHeight; - // The rest of this is cargo-culted to restore the default styling - SystemPalette { - id: myPalette; - colorGroup: SystemPalette.Active - } - color: { - var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base - return styleData.selected?myPalette.highlight:baseColor - } + color: styleData.selected + ? "#afafaf" + : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd } // This Item refers to the contents of each Cell @@ -177,7 +171,6 @@ Item { HifiControls.CheckBox { visible: isCheckBox && !isSeparator; anchors.centerIn: parent; - colorScheme: hifi.colorSchemes.dark; boxSize: 22; onClicked: { var newValue = !model[styleData.role]; diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index b09289c78a..8b940e8178 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -17,6 +17,7 @@ #include "Menu.h" HIFI_QML_DEF(AvatarInputs) +HIFI_QML_DEF(AvatarInputs2) static AvatarInputs* INSTANCE{ nullptr }; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 916556fdd7..9d419e5a0f 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -140,7 +140,7 @@ function populateUserList() { function usernameFromIDReply(id, username, machineFingerprint) { var data; // If the ID we've received is our ID... - if (AvatarList.getAvatar('').sessionUUID === id) { + if (MyAvatar.sessionUUID === id) { // Set the data to contain specific strings. data = ['', username] } else { From 1eea3ed27d5ad7c176d64ff3c33934dc209c6fe3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Dec 2016 12:45:44 -0800 Subject: [PATCH 070/118] No more semicolons; tons of improvements --- .../resources/icons/defaultNameCardUser.png | Bin 50400 -> 3314 bytes .../resources/qml/controls-uit/Table.qml | 2 +- interface/resources/qml/hifi/NameCard.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 290 ++++++++++-------- .../qml/styles-uit/HifiConstants.qml | 4 +- interface/src/ui/AvatarInputs.cpp | 1 - 6 files changed, 164 insertions(+), 137 deletions(-) diff --git a/interface/resources/icons/defaultNameCardUser.png b/interface/resources/icons/defaultNameCardUser.png index 807ca2fa87840b94778e027321d68cfd0288108f..d0c7933db2b26395ac8beffa8377b75b73d74041 100644 GIT binary patch delta 3299 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≫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 diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 865e24945e..8e685dc253 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -52,7 +52,7 @@ TableView { size: hifi.fontSizes.tableHeadingIcon anchors { left: titleText.right - leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 + leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 3 : 0) right: parent.right rightMargin: hifi.dimensions.tablePadding verticalCenter: titleText.verticalCenter diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 4304c407c0..0bc6afd241 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -103,7 +103,7 @@ Row { // Anchors anchors.fill: parent // Style - color: "#eeeeee" + color: "#dbdbdb" // Very appropriate hex value here radius: parent.radius } // Rectangle for the VU meter audio level @@ -112,7 +112,7 @@ Row { // Size width: (nameCardVUMeter.audioLevel) * parent.width // Style - color: "#E3E3E3" + color: "#dbdbdb" // Very appropriate hex value here radius: parent.radius // Anchors anchors.bottom: parent.bottom diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index b62c721ead..35d71b6c42 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -32,116 +32,144 @@ import "../styles-uit" import "../controls-uit" as HifiControls Item { - id: pal; + id: pal // Size - width: parent.width; - height: parent.height; + width: parent.width + height: parent.height // Properties - property int myCardHeight: 70; - property int rowHeight: 70; - property int separatorColWidth: 10; - property int actionButtonWidth: 70; - property int nameCardWidth: width - (iAmAdmin ? separatorColWidth : 0) - actionButtonWidth*(iAmAdmin ? 4 : 2); + property int myCardHeight: 70 + property int rowHeight: 70 + property int separatorColWidth: 10 + property int actionButtonWidth: 75 + property int nameCardWidth: width - (iAmAdmin ? separatorColWidth : 0) - actionButtonWidth*(iAmAdmin ? 4 : 2) // This contains the current user's NameCard and will contain other information in the future Rectangle { - id: myInfo; + id: myInfo // Size - width: pal.width; - height: myCardHeight; + width: pal.width + height: myCardHeight // Anchors - anchors.top: pal.top; + anchors.top: pal.top // Properties - radius: hifi.dimensions.borderRadius; + radius: hifi.dimensions.borderRadius // This NameCard refers to the current user's NameCard (the one above the table) NameCard { - id: myCard; + id: myCard // Properties - displayName: myData.displayName; - userName: myData.userName; + displayName: myData.displayName + userName: myData.userName // Size - width: nameCardWidth; - height: parent.height; + width: nameCardWidth + height: parent.height // Anchors - anchors.left: parent.left; + anchors.left: parent.left } } // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle Rectangle { - color: "#FFFFFF"; - width: pal.width; - height: 10; - anchors.top: myInfo.bottom; - anchors.left: parent.left; + color: "#FFFFFF" + width: pal.width + height: 10 + anchors.top: myInfo.bottom + anchors.left: parent.left } Rectangle { - color: "#FFFFFF"; - width: pal.width; - height: 10; - anchors.bottom: table.top; - anchors.left: parent.left; + color: "#FFFFFF" + width: pal.width + height: 10 + anchors.bottom: table.top + anchors.left: parent.left + } + // Rectangle that houses "Global Actions" string + Rectangle { + visible: iAmAdmin + color: hifi.colors.tableRowLightEven + width: actionButtonWidth * 2 + separatorColWidth - 2 + height: 40 + anchors.bottom: myInfo.bottom + anchors.bottomMargin: -10 + anchors.right: myInfo.right + radius: hifi.dimensions.borderRadius + RalewaySemiBold { + text: "ADMIN" + size: hifi.fontSizes.tableHeading + 2 + font.capitalization: Font.AllUppercase + color: hifi.colors.redHighlight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + anchors.top: parent.top + anchors.topMargin: 8 + anchors.left: parent.left + anchors.right: parent.right + } } // This TableView refers to the table (below the current user's NameCard) HifiControls.Table { - id: table; + id: table // Size - height: pal.height - myInfo.height - 4; - width: pal.width - 4; + height: pal.height - myInfo.height - 4 + width: pal.width - 4 // Anchors - anchors.left: parent.left; - anchors.top: myInfo.bottom; + anchors.left: parent.left + anchors.top: myInfo.bottom // Properties - centerHeaderText: true; - sortIndicatorVisible: true; - headerVisible: true; - onSortIndicatorColumnChanged: sortModel(); - onSortIndicatorOrderChanged: sortModel(); + centerHeaderText: true + sortIndicatorVisible: true + headerVisible: true + onSortIndicatorColumnChanged: sortModel() + onSortIndicatorOrderChanged: sortModel() TableViewColumn { - role: "displayName"; - title: "NAMES"; - width: nameCardWidth; - movable: false; + role: "displayName" + title: "NAMES" + width: nameCardWidth + movable: false + resizable: false } TableViewColumn { - role: "personalMute"; + role: "personalMute" title: "MUTE" - width: actionButtonWidth; - movable: false; + width: actionButtonWidth + movable: false + resizable: false } TableViewColumn { - role: "ignore"; - title: "IGNORE"; - width: actionButtonWidth; - movable: false; + role: "ignore" + title: "IGNORE" + width: actionButtonWidth + movable: false + resizable: false } TableViewColumn { - visible: iAmAdmin; - title: ""; - width: separatorColWidth; - resizable: false; - movable: false; + visible: iAmAdmin + title: "" + width: separatorColWidth + resizable: false + movable: false } TableViewColumn { - visible: iAmAdmin; - role: "mute"; - title: "SILENCE"; - width: actionButtonWidth; - movable: false; + visible: iAmAdmin + role: "mute" + title: "SILENCE" + width: actionButtonWidth + movable: false + resizable: false } TableViewColumn { - visible: iAmAdmin; - role: "kick"; + visible: iAmAdmin + role: "kick" title: "BAN" - width: actionButtonWidth; - movable: false; + width: actionButtonWidth + movable: false + resizable: false } - model: userModel; + model: userModel // This Rectangle refers to each Row in the table. rowDelegate: Rectangle { // The only way I know to specify a row height. // Size - height: rowHeight; + height: rowHeight color: styleData.selected ? "#afafaf" : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd @@ -149,108 +177,108 @@ Item { // This Item refers to the contents of each Cell itemDelegate: Item { - id: itemCell; - property bool isCheckBox: typeof(styleData.value) === 'boolean'; - property bool isSeparator: styleData.value === ''; + id: itemCell + property bool isCheckBox: typeof(styleData.value) === 'boolean' + property bool isSeparator: styleData.value === '' // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { - id: nameCard; + id: nameCard // Properties - displayName: styleData.value; - userName: model.userName; - visible: !isCheckBox && !isSeparator; + displayName: styleData.value + userName: model.userName + visible: !isCheckBox && !isSeparator // Size - width: nameCardWidth; - height: parent.height; + width: nameCardWidth + height: parent.height // Anchors - anchors.left: parent.left; + anchors.left: parent.left } // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) HifiControls.CheckBox { - visible: isCheckBox && !isSeparator; - anchors.centerIn: parent; - boxSize: 22; + visible: isCheckBox && !isSeparator + anchors.centerIn: parent + boxSize: 24 onClicked: { - var newValue = !model[styleData.role]; - var datum = userData[model.userIndex]; - datum[styleData.role] = model[styleData.role] = newValue; - Users[styleData.role](model.sessionId); + var newValue = !model[styleData.role] + var datum = userData[model.userIndex] + datum[styleData.role] = model[styleData.role] = newValue + Users[styleData.role](model.sessionId) // Just for now, while we cannot undo things: - userData.splice(model.userIndex, 1); - sortModel(); + userData.splice(model.userIndex, 1) + sortModel() } } } } // This Rectangle refers to the [?] popup button Rectangle { - color: hifi.colors.tableBackgroundLight; - width: 18; - height: hifi.dimensions.tableHeaderHeight - 2; - anchors.left: table.left; - anchors.top: table.top; - anchors.topMargin: 1; - anchors.leftMargin: nameCardWidth/2 + 23; + color: hifi.colors.tableBackgroundLight + width: 20 + height: hifi.dimensions.tableHeaderHeight - 2 + anchors.left: table.left + anchors.top: table.top + anchors.topMargin: 1 + anchors.leftMargin: nameCardWidth/2 + 24 RalewayRegular { - id: helpText; - text: "[?]"; - size: hifi.fontSizes.tableHeading; - font.capitalization: Font.AllUppercase; - color: hifi.colors.darkGray; - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - anchors.fill: parent; + id: helpText + text: "[?]" + size: hifi.fontSizes.tableHeading + 2 + font.capitalization: Font.AllUppercase + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent } MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; - hoverEnabled: true; - onClicked: namesPopup.visible = true; - onEntered: helpText.color = hifi.colors.baseGrayHighlight; - onExited: helpText.color = hifi.colors.darkGray; + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: namesPopup.visible = true + onEntered: helpText.color = hifi.colors.baseGrayHighlight + onExited: helpText.color = hifi.colors.darkGray } } // Explanitory popup upon clicking "[?]" Item { - visible: false; - id: namesPopup; - anchors.fill: pal; + visible: false + id: namesPopup + anchors.fill: pal Rectangle { - anchors.fill: parent; - color: "black"; - opacity: 0.5; - radius: hifi.dimensions.borderRadius; + anchors.fill: parent + color: "black" + opacity: 0.5 + radius: hifi.dimensions.borderRadius } Rectangle { - width: Math.min(parent.width * 0.75, 400); - height: popupText.contentHeight*2; - anchors.centerIn: parent; - radius: hifi.dimensions.borderRadius; - color: "white"; + width: Math.min(parent.width * 0.75, 400) + height: popupText.contentHeight*2 + anchors.centerIn: parent + radius: hifi.dimensions.borderRadius + color: "white" FiraSansSemiBold { - id: popupText; - text: "This is temporary text. It will eventually be used to explain what 'Names' means."; - size: hifi.fontSizes.textFieldInput; - color: hifi.colors.darkGray; - horizontalAlignment: Text.AlignHCenter; - anchors.fill: parent; - wrapMode: Text.WordWrap; + id: popupText + text: "This is temporary text. It will eventually be used to explain what 'Names' means." + size: hifi.fontSizes.textFieldInput + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + wrapMode: Text.WordWrap } } MouseArea { - anchors.fill: parent; - acceptedButtons: Qt.LeftButton; + anchors.fill: parent + acceptedButtons: Qt.LeftButton onClicked: { - namesPopup.visible = false; + namesPopup.visible = false } } } - property var userData: []; - property var myData: ({displayName: "", userName: ""}); // valid dummy until set - property bool iAmAdmin: false; + property var userData: [] + property var myData: ({displayName: "", userName: ""}) // valid dummy until set + property bool iAmAdmin: false function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var i, data = optionalData || userData, length = data.length; for (var i = 0; i < length; i++) { diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index f2698da574..da1b2868a7 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -89,8 +89,8 @@ Item { readonly property color transparent: "#00ffffff" // Control specific colors - readonly property color tableRowLightOdd: "#eaeaea" // Equivalent to white50 over #e3e3e3 background - readonly property color tableRowLightEven: "#c6c6c6" // Equivavlent to "#1a575757" over #e3e3e3 background + readonly property color tableRowLightOdd: "#fafafa" + readonly property color tableRowLightEven: "#eeeeee" // Equivavlent to "#1a575757" over #e3e3e3 background readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background readonly property color tableBackgroundLight: tableRowLightEven diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 8b940e8178..b09289c78a 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -17,7 +17,6 @@ #include "Menu.h" HIFI_QML_DEF(AvatarInputs) -HIFI_QML_DEF(AvatarInputs2) static AvatarInputs* INSTANCE{ nullptr }; From cf3a260646c64f3a05e88d6e550597afe705dfe8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 21 Dec 2016 13:13:59 -0800 Subject: [PATCH 071/118] added ability to rotate entity and sound direction --- .../tutorials/entity_scripts/ambientSound.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index 6a892ed139..f3eb93c76f 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -8,6 +8,9 @@ // userData.range should be an integer for the max distance away from the entity where the sound will be audible. // userData.volume is the max volume at which the clip should play. Defaults to 1.0 full volume) // +// The rotation of the entity is copied to the ambisonic field, so by rotating the entity you will rotate the +// direction in-which a certain sound comes from. +// // Remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is // large enough to be loaded at the range you set, particularly for large ranges. // @@ -27,6 +30,7 @@ var range = DEFAULT_RANGE; var maxVolume = DEFAULT_VOLUME; var UPDATE_INTERVAL_MSECS = 100; + var rotation; var entity; var ambientSound; @@ -92,23 +96,28 @@ this.maybeUpdate = function() { // Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar _this.updateSettings(); - var props = Entities.getEntityProperties(entity); var HYSTERESIS_FRACTION = 0.1; - var props = Entities.getEntityProperties(entity, [ "position" ]); + var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); center = props.position; + rotation = props.rotation; var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center)); if (distance <= range) { var volume = (1.0 - distance / range) * maxVolume; if (!soundPlaying && ambientSound.downloaded) { - soundPlaying = Audio.playSound(ambientSound, { loop: true, localOnly: true, volume: volume }); + soundPlaying = Audio.playSound(ambientSound, { loop: true, + localOnly: true, + rotation: rotation, + volume: volume }); debugPrint("Starting ambient sound, volume: " + volume); if (WANT_COLOR_CHANGE) { Entities.editEntity(entity, { color: COLOR_ON }); } } else if (soundPlaying && soundPlaying.playing) { - soundPlaying.setOptions( { volume: volume } ); + debugPrint("Setting volume and rotation: " + Quat.safeEulerAngles(rotation).y); + soundPlaying.setOptions( { volume: volume, orientation: rotation } ); } + } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { soundPlaying.stop(); soundPlaying = false; From 632bf997f39b42c527e5ef09a666a7311b5b0da2 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 21 Dec 2016 13:15:44 -0800 Subject: [PATCH 072/118] debug off --- scripts/tutorials/entity_scripts/ambientSound.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index f3eb93c76f..a8e7db239c 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -39,11 +39,11 @@ var checkTimer = false; var _this; - var WANT_COLOR_CHANGE = true; + var WANT_COLOR_CHANGE = false; var COLOR_OFF = { red: 128, green: 128, blue: 128 }; var COLOR_ON = { red: 255, green: 0, blue: 0 }; - var WANT_DEBUG = true; + var WANT_DEBUG = false; function debugPrint(string) { if (WANT_DEBUG) { print(string); @@ -114,7 +114,6 @@ } } else if (soundPlaying && soundPlaying.playing) { - debugPrint("Setting volume and rotation: " + Quat.safeEulerAngles(rotation).y); soundPlaying.setOptions( { volume: volume, orientation: rotation } ); } From b02d14fcc2f899fbb8ce5036b85b22f2affed5ae Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 21 Dec 2016 13:17:08 -0800 Subject: [PATCH 073/118] set right thing initially --- scripts/tutorials/entity_scripts/ambientSound.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index a8e7db239c..620b371400 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -106,7 +106,7 @@ if (!soundPlaying && ambientSound.downloaded) { soundPlaying = Audio.playSound(ambientSound, { loop: true, localOnly: true, - rotation: rotation, + orientation: rotation, volume: volume }); debugPrint("Starting ambient sound, volume: " + volume); if (WANT_COLOR_CHANGE) { From 7c51d47ae437b711798a85bf89a8489fd08ef228 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Dec 2016 14:11:14 -0800 Subject: [PATCH 074/118] Better separator --- interface/resources/qml/hifi/Pal.qml | 57 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 35d71b6c42..af99d9a2e1 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -39,9 +39,8 @@ Item { // Properties property int myCardHeight: 70 property int rowHeight: 70 - property int separatorColWidth: 10 property int actionButtonWidth: 75 - property int nameCardWidth: width - (iAmAdmin ? separatorColWidth : 0) - actionButtonWidth*(iAmAdmin ? 4 : 2) + property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) // This contains the current user's NameCard and will contain other information in the future Rectangle { @@ -81,27 +80,39 @@ Item { anchors.bottom: table.top anchors.left: parent.left } - // Rectangle that houses "Global Actions" string + // Rectangle that houses "ADMIN" string Rectangle { - visible: iAmAdmin - color: hifi.colors.tableRowLightEven - width: actionButtonWidth * 2 + separatorColWidth - 2 + id: adminTab + // Size + width: actionButtonWidth * 2 - 2 height: 40 + // Anchors anchors.bottom: myInfo.bottom anchors.bottomMargin: -10 anchors.right: myInfo.right + // Properties + visible: iAmAdmin + // Style + color: hifi.colors.tableRowLightEven radius: hifi.dimensions.borderRadius + border.color: hifi.colors.lightGrayText + border.width: 2 + // "ADMIN" text RalewaySemiBold { text: "ADMIN" + // Text size size: hifi.fontSizes.tableHeading + 2 - font.capitalization: Font.AllUppercase - color: hifi.colors.redHighlight - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignTop + // Anchors anchors.top: parent.top anchors.topMargin: 8 anchors.left: parent.left anchors.right: parent.right + // Style + font.capitalization: Font.AllUppercase + color: hifi.colors.redHighlight + // Alignment + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop } } // This TableView refers to the table (below the current user's NameCard) @@ -141,13 +152,6 @@ Item { movable: false resizable: false } - TableViewColumn { - visible: iAmAdmin - title: "" - width: separatorColWidth - resizable: false - movable: false - } TableViewColumn { visible: iAmAdmin role: "mute" @@ -179,7 +183,6 @@ Item { itemDelegate: Item { id: itemCell property bool isCheckBox: typeof(styleData.value) === 'boolean' - property bool isSeparator: styleData.value === '' // This NameCard refers to the cell that contains an avatar's // DisplayName and UserName NameCard { @@ -187,7 +190,7 @@ Item { // Properties displayName: styleData.value userName: model.userName - visible: !isCheckBox && !isSeparator + visible: !isCheckBox // Size width: nameCardWidth height: parent.height @@ -197,7 +200,7 @@ Item { // This CheckBox belongs in the columns that contain the action buttons ("Mute", "Ban", etc) HifiControls.CheckBox { - visible: isCheckBox && !isSeparator + visible: isCheckBox anchors.centerIn: parent boxSize: 24 onClicked: { @@ -212,6 +215,18 @@ Item { } } } + // Separator between user and admin functions + Rectangle { + // Size + width: 2 + height: table.height + // Anchors + anchors.left: adminTab.left + anchors.top: table.top + // Properties + visible: iAmAdmin + color: hifi.colors.lightGrayText + } // This Rectangle refers to the [?] popup button Rectangle { color: hifi.colors.tableBackgroundLight @@ -356,7 +371,7 @@ Item { datum[property] = false; } } - ['personalMute', 'ignore', 'spacer', 'mute', 'kick'].forEach(init); + ['personalMute', 'ignore', 'mute', 'kick'].forEach(init); datum.userIndex = userIndex++; userModel.append(datum); }); From 890e35e96ec38e1aa24c892ac4b6a9efdf2bc064 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Dec 2016 15:37:23 -0800 Subject: [PATCH 075/118] cleanup unnecessary scope and swap if-check order --- libraries/animation/src/Rig.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b6bb8ccc8f..eec8035957 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1195,7 +1195,7 @@ bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const void Rig::applyOverridePoses() { PerformanceTimer perfTimer("override"); - if (!_animSkeleton || _numOverrides == 0) { + if (_numOverrides == 0 || !_animSkeleton) { return; } @@ -1218,17 +1218,15 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a ASSERT(_animSkeleton->getNumJoints() == (int)relativePoses.size()); - { - absolutePosesOut.resize(relativePoses.size()); - AnimPose geometryToRigTransform(_geometryToRigTransform); - for (int i = 0; i < (int)relativePoses.size(); i++) { - int parentIndex = _animSkeleton->getParentIndex(i); - if (parentIndex == -1) { - // transform all root absolute poses into rig space - absolutePosesOut[i] = geometryToRigTransform * relativePoses[i]; - } else { - absolutePosesOut[i] = absolutePosesOut[parentIndex] * relativePoses[i]; - } + absolutePosesOut.resize(relativePoses.size()); + AnimPose geometryToRigTransform(_geometryToRigTransform); + for (int i = 0; i < (int)relativePoses.size(); i++) { + int parentIndex = _animSkeleton->getParentIndex(i); + if (parentIndex == -1) { + // transform all root absolute poses into rig space + absolutePosesOut[i] = geometryToRigTransform * relativePoses[i]; + } else { + absolutePosesOut[i] = absolutePosesOut[parentIndex] * relativePoses[i]; } } } From d01500a7bbafcaa8cf608af45f44ed44005abc7e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 21 Dec 2016 17:53:30 -0800 Subject: [PATCH 076/118] Fix the bug? --- interface/resources/qml/hifi/dialogs/RunningScripts.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 3f05a140ae..29807d9646 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -22,7 +22,7 @@ ScrollingWindow { objectName: "RunningScripts" title: "Running Scripts" resizable: true - destroyOnHidden: true + destroyOnHidden: false implicitWidth: 424 implicitHeight: isHMD ? 695 : 728 minSize: Qt.vector2d(424, 300) From b3be7f0f3eedea240de5fd05401ce0dcc2c62977 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Dec 2016 10:42:05 -0800 Subject: [PATCH 077/118] restore expensive version of Avatar::updatePalms() --- interface/src/avatar/Avatar.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a79d2372c5..dc5b6233aa 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1293,23 +1293,10 @@ void Avatar::setOrientation(const glm::quat& orientation) { void Avatar::updatePalms() { PerformanceTimer perfTimer("palms"); // update thread-safe caches - glm::quat rotation; - int i = _skeletonModel->getLeftHandJointIndex(); - if (_skeletonModel->getJointRotationInWorldFrame(i, rotation)) { - _leftPalmRotationCache.set(rotation); - } - glm::vec3 position; - if (_skeletonModel->getJointPositionInWorldFrame(i, position)) { - _leftPalmPositionCache.set(position); - } - - i = _skeletonModel->getRightHandJointIndex(); - if (_skeletonModel->getJointRotationInWorldFrame(i, rotation)) { - _rightPalmRotationCache.set(rotation); - } - if (_skeletonModel->getJointPositionInWorldFrame(i, position)) { - _rightPalmPositionCache.set(position); - } + _leftPalmRotationCache.set(getUncachedLeftPalmRotation()); + _rightPalmRotationCache.set(getUncachedRightPalmRotation()); + _leftPalmPositionCache.set(getUncachedLeftPalmPosition()); + _rightPalmPositionCache.set(getUncachedRightPalmPosition()); } void Avatar::setParentID(const QUuid& parentID) { From 4161775673c5709cbd76d7ee133d284b9f2c60e8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Dec 2016 10:54:58 -0800 Subject: [PATCH 078/118] restore context around lock --- libraries/animation/src/Rig.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index eec8035957..6066905943 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -929,8 +929,10 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); // copy internal poses to external poses - QWriteLocker writeLock(&_externalPoseSetLock); - _externalPoseSet = _internalPoseSet; + { + QWriteLocker writeLock(&_externalPoseSetLock); + _externalPoseSet = _internalPoseSet; + } } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, From e58623bcc0efccb1770c8e5d614722be46fedabe Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Dec 2016 11:46:40 -0800 Subject: [PATCH 079/118] Working on test/trace script interface --- .../src/scripting/TestScriptingInterface.cpp | 78 ++++++++++++++++++- .../src/scripting/TestScriptingInterface.h | 20 ++++- libraries/networking/src/ResourceCache.cpp | 9 +++ libraries/networking/src/ResourceCache.h | 3 + .../oculus/src/OculusBaseDisplayPlugin.cpp | 1 - plugins/oculus/src/OculusDisplayPlugin.cpp | 1 - tests/render-perf/src/main.cpp | 3 + 7 files changed, 109 insertions(+), 6 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 1e1a4d8d0c..8bc601ec83 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -5,15 +5,17 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - - #include "TestScriptingInterface.h" #include #include +#include #include #include +#include + +#include "Application.h" TestScriptingInterface* TestScriptingInterface::getInstance() { static TestScriptingInterface sharedInstance; @@ -25,12 +27,51 @@ void TestScriptingInterface::quit() { } void TestScriptingInterface::waitForTextureIdle() { + waitForCondition(0, []()->bool { + return (0 == gpu::Context::getTextureGPUTransferCount()); + }); } void TestScriptingInterface::waitForDownloadIdle() { + waitForCondition(0, []()->bool { + return (0 == ResourceCache::getLoadingRequestCount()) && (0 == ResourceCache::getPendingRequestCount()); + }); +} + +void TestScriptingInterface::waitForProcessingIdle() { + auto statTracker = DependencyManager::get(); + waitForCondition(0, [statTracker]()->bool { + return (0 == statTracker->getStat("Processing").toInt() && 0 == statTracker->getStat("PendingProcessing").toInt()); + }); } void TestScriptingInterface::waitIdle() { + // Initial wait for some incoming work + QThread::sleep(1); + waitForDownloadIdle(); + waitForProcessingIdle(); + waitForTextureIdle(); +} + +bool TestScriptingInterface::loadTestScene(QString scene) { + // FIXME implement + // qApp->postLambdaEvent([isClient] { + // auto tree = qApp->getEntityClipboard(); + // tree->setIsClient(isClient); + // }); + /* + Test.setClientTree(false); + Resources.overrideUrlPrefix("atp:/", TEST_BINARY_ROOT + scene + ".atp/"); + if (!Clipboard.importEntities(TEST_SCENES_ROOT + scene + ".json")) { + return false; + } + var position = { x: 0, y: 0, z: 0 }; + var pastedEntityIDs = Clipboard.pasteEntities(position); + for (var id in pastedEntityIDs) { + print("QQQ ID imported " + id); + } + */ + return false; } bool TestScriptingInterface::startTracing(QString logrules) { @@ -55,4 +96,35 @@ bool TestScriptingInterface::stopTracing(QString filename) { tracer->stopTracing(); tracer->serialize(filename); return true; -} \ No newline at end of file +} + +void TestScriptingInterface::clear() { + qApp->postLambdaEvent([] { + qApp->getEntities()->clear(); + }); +} + +bool TestScriptingInterface::waitForConnection(qint64 maxWaitMs) { + // Wait for any previous connection to die + QThread::sleep(1); + return waitForCondition(maxWaitMs, []()->bool { + return DependencyManager::get()->getDomainHandler().isConnected(); + }); +} + +void TestScriptingInterface::wait(int milliseconds) { + QThread::msleep(milliseconds); +} + +bool TestScriptingInterface::waitForCondition(qint64 maxWaitMs, std::function condition) { + QElapsedTimer elapsed; + elapsed.start(); + while (!condition()) { + if (maxWaitMs > 0 && elapsed.elapsed() > maxWaitMs) { + return false; + } + QThread::msleep(1); + } + return condition(); +} + diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 0c6ab6baaa..6307289ac2 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -10,6 +10,7 @@ #ifndef hifi_TestScriptingInterface_h #define hifi_TestScriptingInterface_h +#include #include class TestScriptingInterface : public QObject { @@ -34,10 +35,24 @@ public slots: void waitForDownloadIdle(); /**jsdoc - * Waits for all pending downloads and texture transfers to be complete + * Waits for all file parsing operations to be complete + */ + void waitForProcessingIdle(); + + /**jsdoc + * Waits for all pending downloads, parsing and texture transfers to be complete */ void waitIdle(); + + bool waitForConnection(qint64 maxWaitMs = 10000); + + void wait(int milliseconds); + + bool loadTestScene(QString sceneFile); + + void clear(); + /**jsdoc * Start recording Chrome compatible tracing events * logRules can be used to specify a set of logging category rules to limit what gets captured @@ -49,6 +64,9 @@ public slots: * Using a filename with a .gz extension will automatically compress the output file */ bool stopTracing(QString filename); + +private: + bool waitForCondition(qint64 maxWaitMs, std::function condition); }; #endif // hifi_TestScriptingInterface_h diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 677be8ecf5..d95c6f140f 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -71,6 +71,11 @@ QList> ResourceCacheSharedItems::getLoadingRequests() { return result; } +uint32_t ResourceCacheSharedItems::getLoadingRequestsCount() const { + Lock lock(_mutex); + return _loadingRequests.size(); +} + void ResourceCacheSharedItems::removeRequest(QWeakPointer resource) { Lock lock(_mutex); @@ -463,6 +468,10 @@ int ResourceCache::getPendingRequestCount() { return DependencyManager::get()->getPendingRequestsCount(); } +int ResourceCache::getLoadingRequestCount() { + return DependencyManager::get()->getLoadingRequestsCount(); +} + bool ResourceCache::attemptRequest(QSharedPointer resource) { Q_ASSERT(!resource.isNull()); auto sharedItems = DependencyManager::get(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 4231785616..03d37bc9ac 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -73,6 +73,7 @@ public: uint32_t getPendingRequestsCount() const; QList> getLoadingRequests(); QSharedPointer getHighestPendingRequest(); + uint32_t getLoadingRequestsCount() const; private: ResourceCacheSharedItems() = default; @@ -241,6 +242,8 @@ public: static int getPendingRequestCount(); + static int getLoadingRequestCount(); + ResourceCache(QObject* parent = nullptr); virtual ~ResourceCache(); diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 5db5840f43..26906ef2fb 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -137,5 +137,4 @@ void OculusBaseDisplayPlugin::updatePresentPose() { } OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() { - qDebug() << "Destroying OculusBaseDisplayPlugin"; } diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 2e9f74a0e5..88ca02edc2 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -183,5 +183,4 @@ QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { } OculusDisplayPlugin::~OculusDisplayPlugin() { - qDebug() << "Destroying OculusDisplayPlugin"; } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index bd731564e7..31bb13f4d0 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -475,6 +476,8 @@ public: DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(NodeType::Agent); DependencyManager::set(); From be6b098e1b376c7d406dbec667a9ad572b9301c9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Dec 2016 13:12:59 -0800 Subject: [PATCH 080/118] Implement test scene loading --- .../src/scripting/TestScriptingInterface.cpp | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 8bc601ec83..3d9e84ce63 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "Application.h" @@ -54,24 +55,20 @@ void TestScriptingInterface::waitIdle() { } bool TestScriptingInterface::loadTestScene(QString scene) { - // FIXME implement - // qApp->postLambdaEvent([isClient] { - // auto tree = qApp->getEntityClipboard(); - // tree->setIsClient(isClient); - // }); - /* - Test.setClientTree(false); - Resources.overrideUrlPrefix("atp:/", TEST_BINARY_ROOT + scene + ".atp/"); - if (!Clipboard.importEntities(TEST_SCENES_ROOT + scene + ".json")) { - return false; - } - var position = { x: 0, y: 0, z: 0 }; - var pastedEntityIDs = Clipboard.pasteEntities(position); - for (var id in pastedEntityIDs) { - print("QQQ ID imported " + id); - } - */ - return false; + static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/"; + static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/"; + static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/"; + static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/"; + return DependencyManager::get()->returnFromUiThread([scene]()->QVariant { + ResourceManager::setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/"); + auto tree = qApp->getEntities()->getTree(); + auto treeIsClient = tree->getIsClient(); + // Force the tree to accept the load regardless of permissions + tree->setIsClient(false); + auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json"); + tree->setIsClient(treeIsClient); + return result; + }).toBool(); } bool TestScriptingInterface::startTracing(QString logrules) { From 2e897e0cc9fc0dcd1788c0303d1b688688af6e74 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 22 Dec 2016 14:01:03 -0800 Subject: [PATCH 081/118] Working first pass Seems roughly same as mic meter. Works for other avatars, though using agent avatars (crowd-agent.js + summon.js), seems not to work. I'll investigate that... --- interface/resources/qml/hifi/NameCard.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 16 +++++++- scripts/system/pal.js | 46 ++++++++++++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0bc6afd241..9b90ae6c3b 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -33,6 +33,7 @@ Row { property string userName: "" property int displayTextHeight: 18 property int usernameTextHeight: 12 + property real audioLevel: 0.0 Column { id: avatarImage @@ -91,7 +92,6 @@ Row { // VU Meter Rectangle { // CHANGEME to the appropriate type! id: nameCardVUMeter - objectName: "AvatarInputs" // Size width: parent.width height: 8 @@ -110,7 +110,7 @@ Row { Rectangle { id: vuMeterLevel // Size - width: (nameCardVUMeter.audioLevel) * parent.width + width: (thisNameCard.audioLevel) * parent.width // Style color: "#dbdbdb" // Very appropriate hex value here radius: parent.radius diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index af99d9a2e1..aac3cc2e2c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -58,6 +58,7 @@ Item { // Properties displayName: myData.displayName userName: myData.userName + audioLevel: myData.audioLevel // Size width: nameCardWidth height: parent.height @@ -190,6 +191,7 @@ Item { // Properties displayName: styleData.value userName: model.userName + audioLevel: model.audioLevel visible: !isCheckBox // Size width: nameCardWidth @@ -292,7 +294,7 @@ Item { } property var userData: [] - property var myData: ({displayName: "", userName: ""}) // valid dummy until set + property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property bool iAmAdmin: false function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var i, data = optionalData || userData, length = data.length; @@ -343,6 +345,18 @@ Item { userData[userIndex].userName = userName; // Defensive programming } break; + case 'updateAudioLevel': + var userId = message.params[0]; + var audioLevel = message.params[1]; + if (!userId) { + myData.audioLevel = audioLevel; + myCard.audioLevel = audioLevel; + } else { + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).audioLevel = audioLevel; + userData[userIndex].audioLevel = audioLevel; + } + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 9d419e5a0f..14b3f483da 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -119,7 +119,8 @@ function populateUserList() { var avatarPalDatum = { displayName: avatar.sessionDisplayName, userName: '', - sessionId: id || '' + sessionId: id || '', + audioLevel: getAudioLevel(id) }; // If the current user is an admin OR // they're requesting their own username ("id" is blank)... @@ -262,6 +263,49 @@ function onClicked() { pal.setVisible(!pal.visible); } +var AVERAGING_RATIO = 0.05 +var LOUDNESS_FLOOR = 11.0; +var LOUDNESS_SCALE = 2.8 / 5.0; +var LOG2 = Math.log(2.0); +var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) +var accumulatedLevels={}; + +function getAudioLevel(id) { + // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged + // But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency + // of updating (the latter for efficiency too). + var avatar = AvatarList.getAvatar(id); + var audioLevel = 0.0; + + // we will do exponential moving average by taking some the last loudness and averaging + accumulatedLevels[id] = AVERAGING_RATIO*(accumulatedLevels[id] || 0 ) + (1-AVERAGING_RATIO)*(avatar.audioLoudness); + + // add 1 to insure we don't go log() and hit -infinity. Math.log is + // natural log, so to get log base 2, just divide by ln(2). + var logLevel = Math.log(accumulatedLevels[id]+1) / LOG2; + + if (logLevel <= LOUDNESS_FLOOR) { + audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; + } else { + audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; + } + if (audioLevel > 1.0) { + audioLevel = 1; + } + return audioLevel; +} + + +// we will update the audioLevels periodically +// TODO: tune for efficiency - expecially with large numbers of avatars +Script.setInterval(function () { + if (pal.visible) { + AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { + var level = getAudioLevel(id); + pal.sendToQml({method: 'updateAudioLevel', params: [id, level]}); + }); + } +}, AUDIO_LEVEL_UPDATE_INTERVAL_MS); // // Button state. // From a5efc08473ed05209a89a782617e3dde77c4eaf1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Dec 2016 15:32:30 -0800 Subject: [PATCH 082/118] use PROFILE_COUNTER not SAMPLE_PROFILE_COUNTER --- interface/src/avatar/AvatarManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 53f17e9635..c5222641ff 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -170,9 +170,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumAvatarsPerSec", + PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); - SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); + PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { From fc4763be038f06885c15628bf1394b8263d3f0f2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Dec 2016 15:55:17 -0800 Subject: [PATCH 083/118] use PROFILE_COUNTER not SAMPLE_PROFILE_COUNTER --- interface/src/avatar/AvatarManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 53f17e9635..c5222641ff 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -170,9 +170,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // simulate avatar fades simulateAvatarFades(deltaTime); - SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumAvatarsPerSec", + PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); - SAMPLE_PROFILE_COUNTER(0.1f, simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); + PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); } void AvatarManager::postUpdate(float deltaTime) { From 966cbb768dca3fc3de1cc8f8b1abe4f0fc2d290a Mon Sep 17 00:00:00 2001 From: Christoph Haag Date: Fri, 23 Dec 2016 01:02:58 +0100 Subject: [PATCH 084/118] fix GL 4.5 when glTextureSubImage2DEXT unavailable For example mesa --- libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 893d18f541..9b16908244 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -398,9 +398,13 @@ bool GL45Texture::continueTransfer() { glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); } else if (GL_TEXTURE_CUBE_MAP == _target) { // DSA ARB does not work on AMD, so use EXT - // glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); - auto target = CUBE_FACE_LAYOUT[face]; - glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + // unless EXT is not available on the driver + if (glTextureSubImage2DEXT) { + auto target = CUBE_FACE_LAYOUT[face]; + glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); + } else { + glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData()); + } } else { Q_ASSERT(false); } From f2bdbe051c140e7af4643d2af11fd4de16fe10dc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 22 Dec 2016 16:35:12 -0800 Subject: [PATCH 085/118] Halve the volume --- scripts/system/bubble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index f02f69729b..2f7286872e 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -64,7 +64,7 @@ Audio.playSound(bubbleActivateSound, { position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }, localOnly: true, - volume: 0.4 + volume: 0.2 }); hideOverlays(); if (updateConnected === true) { From 9eed430f49340c7aab2fad59d3a81a9334c5574e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 22 Dec 2016 18:09:19 -0800 Subject: [PATCH 086/118] CR feedback --- interface/resources/qml/hifi/Pal.qml | 21 ++++++++++++--------- scripts/system/pal.js | 8 ++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index aac3cc2e2c..a03556a56e 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -346,15 +346,18 @@ Item { } break; case 'updateAudioLevel': - var userId = message.params[0]; - var audioLevel = message.params[1]; - if (!userId) { - myData.audioLevel = audioLevel; - myCard.audioLevel = audioLevel; - } else { - var userIndex = findSessionIndex(userId); - userModel.get(userIndex).audioLevel = audioLevel; - userData[userIndex].audioLevel = audioLevel; + for (var userId in message.params) { + var audioLevel = message.params[userId]; + // If the userId is 0, we're updating "myData". + if (userId == 0) { + myData.audioLevel = audioLevel; + myCard.audioLevel = audioLevel; // Defensive programming + } else { + console.log("userid:" + userId); + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).audioLevel = audioLevel; + userData[userIndex].audioLevel = audioLevel; // Defensive programming + } } break; default: diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 14b3f483da..a19c6a8c29 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -120,7 +120,7 @@ function populateUserList() { displayName: avatar.sessionDisplayName, userName: '', sessionId: id || '', - audioLevel: getAudioLevel(id) + audioLevel: 0.0 }; // If the current user is an admin OR // they're requesting their own username ("id" is blank)... @@ -300,10 +300,14 @@ function getAudioLevel(id) { // TODO: tune for efficiency - expecially with large numbers of avatars Script.setInterval(function () { if (pal.visible) { + var param = {}; AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { var level = getAudioLevel(id); - pal.sendToQml({method: 'updateAudioLevel', params: [id, level]}); + // qml didn't like an object with null/empty string for a key, so... + var userId = id || 0; + param[userId]= level; }); + pal.sendToQml({method: 'updateAudioLevel', params: param}); } }, AUDIO_LEVEL_UPDATE_INTERVAL_MS); // From 5490dddbbf6fdb64fb4c2ca5096b23a195ed3865 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 23 Dec 2016 12:22:33 -0800 Subject: [PATCH 087/118] Cool style changes! --- interface/resources/qml/hifi/Pal.qml | 42 +++++++++++++++++++++++++++- scripts/system/pal.js | 10 +++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index af99d9a2e1..69e4b1d3e7 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -47,7 +47,7 @@ Item { id: myInfo // Size width: pal.width - height: myCardHeight + height: myCardHeight + 20 // Anchors anchors.top: pal.top // Properties @@ -215,6 +215,46 @@ Item { } } } + // Refresh button + Rectangle { + // Size + width: hifi.dimensions.tableHeaderHeight-1 + height: hifi.dimensions.tableHeaderHeight-1 + // Anchors + anchors.left: table.left + anchors.leftMargin: 4 + anchors.top: table.top + // Style + color: hifi.colors.tableBackgroundLight + // Actual refresh icon + HiFiGlyphs { + id: reloadButton + text: hifi.glyphs.reloadSmall + // Size + size: parent.width*1.5 + // Anchors + anchors.fill: parent + // Style + horizontalAlignment: Text.AlignHCenter + color: hifi.colors.darkGray + } + MouseArea { + id: reloadButtonArea + // Anchors + anchors.fill: parent + hoverEnabled: true + // Everyone likes a responsive refresh button! + // So use onPressed instead of onClicked + onPressed: { + reloadButton.color = hifi.colors.lightGrayText + pal.sendToScript({method: 'refresh'}) + } + onReleased: reloadButton.color = (containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.darkGray) + onEntered: reloadButton.color = hifi.colors.baseGrayHighlight + onExited: reloadButton.color = (pressed ? hifi.colors.lightGrayText: hifi.colors.darkGray) + } + } + // Separator between user and admin functions Rectangle { // Size diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 9d419e5a0f..be87069b54 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -94,6 +94,10 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like overlay.select(selected); }); break; + case 'refresh': + removeOverlays(); + populateUserList(); + break; default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } @@ -265,13 +269,13 @@ function onClicked() { // // Button state. // -function onVisibileChanged() { +function onVisibleChanged() { button.writeProperty('buttonState', pal.visible ? 0 : 1); button.writeProperty('defaultState', pal.visible ? 0 : 1); button.writeProperty('hoverState', pal.visible ? 2 : 3); } button.clicked.connect(onClicked); -pal.visibleChanged.connect(onVisibileChanged); +pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); Users.usernameFromIDReply.connect(usernameFromIDReply); @@ -281,7 +285,7 @@ Users.usernameFromIDReply.connect(usernameFromIDReply); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); toolBar.removeButton(buttonName); - pal.visibleChanged.disconnect(onVisibileChanged); + pal.visibleChanged.disconnect(onVisibleChanged); pal.closed.disconnect(off); Users.usernameFromIDReply.disconnect(usernameFromIDReply); off(); From 1cb330057e8ba1a88eff78815c4ca597301cdac2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 23 Dec 2016 13:05:57 -0800 Subject: [PATCH 088/118] fix a crash and a memory leak in vhacd-util --- libraries/shared/src/Trace.h | 5 ++++- tools/vhacd-util/src/VHACDUtil.cpp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/Trace.h b/libraries/shared/src/Trace.h index f719969758..ee4f28f0ce 100644 --- a/libraries/shared/src/Trace.h +++ b/libraries/shared/src/Trace.h @@ -101,7 +101,10 @@ private: }; inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) { - DependencyManager::get()->traceEvent(category, name, type, id, args, extra); + const auto& tracer = DependencyManager::get(); + if (tracer) { + tracer->traceEvent(category, name, type, id, args, extra); + } } inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, int id, const QVariantMap& args = {}, const QVariantMap& extra = {}) { diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 9e28d33120..30d0b5e772 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -51,6 +51,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { return false; } result = *geom; + delete geom; reSortFBXGeometryMeshes(result); } catch (const QString& error) { From 5065f027e79787b6da58385756ac36cfda93e5a1 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 23 Dec 2016 13:11:42 -0800 Subject: [PATCH 089/118] whitespace --- scripts/system/pal.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a19c6a8c29..2b24a76cae 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -268,7 +268,7 @@ var LOUDNESS_FLOOR = 11.0; var LOUDNESS_SCALE = 2.8 / 5.0; var LOG2 = Math.log(2.0); var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) -var accumulatedLevels={}; +var accumulatedLevels = {}; function getAudioLevel(id) { // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged @@ -278,11 +278,11 @@ function getAudioLevel(id) { var audioLevel = 0.0; // we will do exponential moving average by taking some the last loudness and averaging - accumulatedLevels[id] = AVERAGING_RATIO*(accumulatedLevels[id] || 0 ) + (1-AVERAGING_RATIO)*(avatar.audioLoudness); + accumulatedLevels[id] = AVERAGING_RATIO * (accumulatedLevels[id] || 0 ) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness); // add 1 to insure we don't go log() and hit -infinity. Math.log is // natural log, so to get log base 2, just divide by ln(2). - var logLevel = Math.log(accumulatedLevels[id]+1) / LOG2; + var logLevel = Math.log(accumulatedLevels[id] + 1) / LOG2; if (logLevel <= LOUDNESS_FLOOR) { audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; From b878051cd94d971463e8b4df794d7605f51ec74f Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 24 Dec 2016 08:42:56 -0800 Subject: [PATCH 090/118] Ambisonic limiter, with gain linking between all channels to preserve imaging --- libraries/audio/src/AudioLimiter.cpp | 164 ++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 5 deletions(-) diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index 7bbaca62ca..e0808c1daa 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -211,6 +211,49 @@ static inline int32_t peaklog2(float* input0, float* input1) { return (e << LOG2_FRACBITS) - (c2 >> 3); } +// +// Peak detection and -log2(x) for float input (quad) +// x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff +// x > 2^LOG2_HEADROOM undefined +// +static inline int32_t peaklog2(float* input0, float* input1, float* input2, float* input3) { + + // float as integer bits + int32_t u0 = *(int32_t*)input0; + int32_t u1 = *(int32_t*)input1; + int32_t u2 = *(int32_t*)input2; + int32_t u3 = *(int32_t*)input3; + + // max absolute value + u0 &= IEEE754_FABS_MASK; + u1 &= IEEE754_FABS_MASK; + u2 &= IEEE754_FABS_MASK; + u3 &= IEEE754_FABS_MASK; + int32_t peak = MAX(MAX(u0, u1), MAX(u2, u3)); + + // split into e and x - 1.0 + int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM; + int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff; + + // saturate + if (e > 31) { + return 0x7fffffff; + } + + int k = x >> (31 - LOG2_TABBITS); + + // polynomial for log2(1+x) over x=[0,1] + int32_t c0 = log2Table[k][0]; + int32_t c1 = log2Table[k][1]; + int32_t c2 = log2Table[k][2]; + + c1 += MULHI(c0, x); + c2 += MULHI(c1, x); + + // reconstruct result in Q26 + return (e << LOG2_FRACBITS) - (c2 >> 3); +} + // // Compute exp2(-x) for x=[0,32] in Q26, result in Q31 // x < 0 undefined @@ -376,6 +419,39 @@ public: } }; +// +// N-1 sample delay (quad) +// +template +class QuadDelay { + + static_assert((N & (N - 1)) == 0, "N must be a power of 2"); + + float _buffer[4*N] = {}; + int _index = 0; + +public: + void process(float& x0, float& x1, float& x2, float& x3) { + + const int MASK = 4*N - 1; // buffer wrap + int i = _index; + + _buffer[i+0] = x0; + _buffer[i+1] = x1; + _buffer[i+2] = x2; + _buffer[i+3] = x3; + + i = (i + 4*(N - 1)) & MASK; + + x0 = _buffer[i+0]; + x1 = _buffer[i+1]; + x2 = _buffer[i+2]; + x3 = _buffer[i+3]; + + _index = i; + } +}; + // // Limiter (common) // @@ -428,7 +504,7 @@ LimiterImpl::LimiterImpl(int sampleRate) { // void LimiterImpl::setThreshold(float threshold) { - const double OUT_CEILING = -0.3; + const double OUT_CEILING = -0.3; // cannot be 0.0, due to dither const double Q31_TO_Q15 = 32768 / 2147483648.0; // limiter threshold = -48dB to 0dB @@ -571,8 +647,8 @@ public: }; template -void LimiterMono::process(float* input, int16_t* output, int numFrames) -{ +void LimiterMono::process(float* input, int16_t* output, int numFrames) { + for (int n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain @@ -623,8 +699,8 @@ public: }; template -void LimiterStereo::process(float* input, int16_t* output, int numFrames) -{ +void LimiterStereo::process(float* input, int16_t* output, int numFrames) { + for (int n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain @@ -663,6 +739,71 @@ void LimiterStereo::process(float* input, int16_t* output, int numFrames) } } +// +// Limiter (quad) +// +template +class LimiterQuad : public LimiterImpl { + + PeakFilter _filter; + QuadDelay _delay; + +public: + LimiterQuad(int sampleRate) : LimiterImpl(sampleRate) {} + + // interleaved quad input/output + void process(float* input, int16_t* output, int numFrames) override; +}; + +template +void LimiterQuad::process(float* input, int16_t* output, int numFrames) { + + for (int n = 0; n < numFrames; n++) { + + // peak detect and convert to log2 domain + int32_t peak = peaklog2(&input[4*n+0], &input[4*n+1], &input[4*n+2], &input[4*n+3]); + + // compute limiter attenuation + int32_t attn = MAX(_threshold - peak, 0); + + // apply envelope + attn = envelope(attn); + + // convert from log2 domain + attn = fixexp2(attn); + + // lowpass filter + attn = _filter.process(attn); + float gain = attn * _outGain; + + // delay audio + float x0 = input[4*n+0]; + float x1 = input[4*n+1]; + float x2 = input[4*n+2]; + float x3 = input[4*n+3]; + _delay.process(x0, x1, x2, x3); + + // apply gain + x0 *= gain; + x1 *= gain; + x2 *= gain; + x3 *= gain; + + // apply dither + float d = dither(); + x0 += d; + x1 += d; + x2 += d; + x3 += d; + + // store 16-bit output + output[4*n+0] = (int16_t)floatToInt(x0); + output[4*n+1] = (int16_t)floatToInt(x1); + output[4*n+2] = (int16_t)floatToInt(x2); + output[4*n+3] = (int16_t)floatToInt(x3); + } +} + // // Public API // @@ -695,6 +836,19 @@ AudioLimiter::AudioLimiter(int sampleRate, int numChannels) { _impl = new LimiterStereo<128>(sampleRate); } + } else if (numChannels == 4) { + + // ~1.5ms lookahead for all rates + if (sampleRate < 16000) { + _impl = new LimiterQuad<16>(sampleRate); + } else if (sampleRate < 32000) { + _impl = new LimiterQuad<32>(sampleRate); + } else if (sampleRate < 64000) { + _impl = new LimiterQuad<64>(sampleRate); + } else { + _impl = new LimiterQuad<128>(sampleRate); + } + } else { assert(0); // unsupported } From f5d52c3d3be239680fd5d76caed0ee72ec3a3a21 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 24 Dec 2016 09:00:23 -0800 Subject: [PATCH 091/118] 64-bit code optimizations Use size_t for inner-loop array indexing, to avoid extraneous MOVSXD instructions when compiled with MSVC x64. --- libraries/audio/src/AudioLimiter.cpp | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index e0808c1daa..775e6f0c76 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -158,7 +158,7 @@ static inline int32_t peaklog2(float* input) { return 0x7fffffff; } - int k = x >> (31 - LOG2_TABBITS); + size_t k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -197,7 +197,7 @@ static inline int32_t peaklog2(float* input0, float* input1) { return 0x7fffffff; } - int k = x >> (31 - LOG2_TABBITS); + size_t k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -240,7 +240,7 @@ static inline int32_t peaklog2(float* input0, float* input1, float* input2, floa return 0x7fffffff; } - int k = x >> (31 - LOG2_TABBITS); + size_t k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -264,7 +264,7 @@ static inline int32_t fixexp2(int32_t x) { int32_t e = x >> LOG2_FRACBITS; x = ~(x << LOG2_INTBITS) & 0x7fffffff; - int k = x >> (31 - EXP2_TABBITS); + size_t k = x >> (31 - EXP2_TABBITS); // polynomial for exp2(x) int32_t c0 = exp2Table[k][0]; @@ -301,7 +301,7 @@ class PeakFilterT { static_assert((CIC1 - 1) + (CIC2 - 1) == (N - 1), "Total CIC delay must be N-1"); int32_t _buffer[2*N] = {}; // shared FIFO - int _index = 0; + size_t _index = 0; int32_t _acc1 = 0; // CIC1 integrator int32_t _acc2 = 0; // CIC2 integrator @@ -310,21 +310,21 @@ public: PeakFilterT() { // fill history - for (int n = 0; n < N-1; n++) { + for (size_t n = 0; n < N-1; n++) { process(0x7fffffff); } } int32_t process(int32_t x) { - const int MASK = 2*N - 1; // buffer wrap - int i = _index; + const size_t MASK = 2*N - 1; // buffer wrap + size_t i = _index; // Fast peak-hold using a running-min filter. Finds the peak (min) value // in the sliding window of N-1 samples, using only log2(N) comparisons. // Hold time of N-1 samples exactly cancels the step response of FIR filter. - for (int n = 1; n < N; n <<= 1) { + for (size_t n = 1; n < N; n <<= 1) { _buffer[i] = x; i = (i + n) & MASK; @@ -372,13 +372,13 @@ class MonoDelay { static_assert((N & (N - 1)) == 0, "N must be a power of 2"); float _buffer[N] = {}; - int _index = 0; + size_t _index = 0; public: void process(float& x) { - const int MASK = N - 1; // buffer wrap - int i = _index; + const size_t MASK = N - 1; // buffer wrap + size_t i = _index; _buffer[i] = x; @@ -399,13 +399,13 @@ class StereoDelay { static_assert((N & (N - 1)) == 0, "N must be a power of 2"); float _buffer[2*N] = {}; - int _index = 0; + size_t _index = 0; public: void process(float& x0, float& x1) { - const int MASK = 2*N - 1; // buffer wrap - int i = _index; + const size_t MASK = 2*N - 1; // buffer wrap + size_t i = _index; _buffer[i+0] = x0; _buffer[i+1] = x1; @@ -428,13 +428,13 @@ class QuadDelay { static_assert((N & (N - 1)) == 0, "N must be a power of 2"); float _buffer[4*N] = {}; - int _index = 0; + size_t _index = 0; public: void process(float& x0, float& x1, float& x2, float& x3) { - const int MASK = 4*N - 1; // buffer wrap - int i = _index; + const size_t MASK = 4*N - 1; // buffer wrap + size_t i = _index; _buffer[i+0] = x0; _buffer[i+1] = x1; @@ -547,7 +547,7 @@ void LimiterImpl::setRelease(float release) { double x = MAXHOLD * _sampleRate; double xstep = x / NHOLD; // 1.0 to 1.0/NHOLD - int i = 0; + size_t i = 0; for (; i < NHOLD; i++) { // max release @@ -613,12 +613,12 @@ int32_t LimiterImpl::envelope(int32_t attn) { // arc = (attn-rms)*6/attn for attn = 1dB to 6dB // arc = (attn-rms)*6/6 for attn > 6dB - int bits = MIN(attn >> 20, 0x3f); // saturate 1/attn at 6dB - _arc = MAX(attn - _rms, 0); // peak/rms = (attn-rms) - _arc = MULHI(_arc, invTable[bits]); // normalized peak/rms = (attn-rms)/attn - _arc = MIN(_arc, NARC - 1); // saturate at 6dB + size_t bits = MIN(attn >> 20, 0x3f); // saturate 1/attn at 6dB + _arc = MAX(attn - _rms, 0); // peak/rms = (attn-rms) + _arc = MULHI(_arc, invTable[bits]); // normalized peak/rms = (attn-rms)/attn + _arc = MIN(_arc, NARC - 1); // saturate at 6dB - _arcRelease = 0x7fffffff; // reset release + _arcRelease = 0x7fffffff; // reset release } _attn = attn; @@ -649,7 +649,7 @@ public: template void LimiterMono::process(float* input, int16_t* output, int numFrames) { - for (int n = 0; n < numFrames; n++) { + for (size_t n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[n]); @@ -701,7 +701,7 @@ public: template void LimiterStereo::process(float* input, int16_t* output, int numFrames) { - for (int n = 0; n < numFrames; n++) { + for (size_t n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[2*n+0], &input[2*n+1]); @@ -758,7 +758,7 @@ public: template void LimiterQuad::process(float* input, int16_t* output, int numFrames) { - for (int n = 0; n < numFrames; n++) { + for (size_t n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[4*n+0], &input[4*n+1], &input[4*n+2], &input[4*n+3]); From fc2c2a190652680b008d982ac0fdd3e01ac71992 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 24 Dec 2016 10:06:30 -0800 Subject: [PATCH 092/118] fix compiler warnings --- libraries/audio/src/AudioLimiter.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp index 775e6f0c76..316c0b181a 100644 --- a/libraries/audio/src/AudioLimiter.cpp +++ b/libraries/audio/src/AudioLimiter.cpp @@ -158,7 +158,7 @@ static inline int32_t peaklog2(float* input) { return 0x7fffffff; } - size_t k = x >> (31 - LOG2_TABBITS); + int k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -197,7 +197,7 @@ static inline int32_t peaklog2(float* input0, float* input1) { return 0x7fffffff; } - size_t k = x >> (31 - LOG2_TABBITS); + int k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -240,7 +240,7 @@ static inline int32_t peaklog2(float* input0, float* input1, float* input2, floa return 0x7fffffff; } - size_t k = x >> (31 - LOG2_TABBITS); + int k = x >> (31 - LOG2_TABBITS); // polynomial for log2(1+x) over x=[0,1] int32_t c0 = log2Table[k][0]; @@ -264,7 +264,7 @@ static inline int32_t fixexp2(int32_t x) { int32_t e = x >> LOG2_FRACBITS; x = ~(x << LOG2_INTBITS) & 0x7fffffff; - size_t k = x >> (31 - EXP2_TABBITS); + int k = x >> (31 - EXP2_TABBITS); // polynomial for exp2(x) int32_t c0 = exp2Table[k][0]; @@ -547,7 +547,7 @@ void LimiterImpl::setRelease(float release) { double x = MAXHOLD * _sampleRate; double xstep = x / NHOLD; // 1.0 to 1.0/NHOLD - size_t i = 0; + int i = 0; for (; i < NHOLD; i++) { // max release @@ -649,7 +649,7 @@ public: template void LimiterMono::process(float* input, int16_t* output, int numFrames) { - for (size_t n = 0; n < numFrames; n++) { + for (int n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[n]); @@ -701,7 +701,7 @@ public: template void LimiterStereo::process(float* input, int16_t* output, int numFrames) { - for (size_t n = 0; n < numFrames; n++) { + for (int n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[2*n+0], &input[2*n+1]); @@ -758,7 +758,7 @@ public: template void LimiterQuad::process(float* input, int16_t* output, int numFrames) { - for (size_t n = 0; n < numFrames; n++) { + for (int n = 0; n < numFrames; n++) { // peak detect and convert to log2 domain int32_t peak = peaklog2(&input[4*n+0], &input[4*n+1], &input[4*n+2], &input[4*n+3]); From a2ea6bf36e622912cd3bbc5277d05fa315a7b8c9 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 24 Dec 2016 11:10:35 -0800 Subject: [PATCH 093/118] Improved audio resampler Added LQ mode (2x faster). Added HQ mode (2x slower), intended for offline resampling. Default (MQ) quality is slightly improved (512 filter phases in irrational mode). --- libraries/audio/src/AudioSRC.cpp | 31 +- libraries/audio/src/AudioSRC.h | 14 +- libraries/audio/src/AudioSRCData.h | 3663 ++++++++++++++++++++++++---- 3 files changed, 3164 insertions(+), 544 deletions(-) diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp index 3cd7a53b6b..80cb756d04 100644 --- a/libraries/audio/src/AudioSRC.cpp +++ b/libraries/audio/src/AudioSRC.cpp @@ -99,18 +99,22 @@ static void cubicInterpolation(const float* input, float* output, int inputSize, } } -int AudioSRC::createRationalFilter(int upFactor, int downFactor, float gain) { - int numTaps = PROTOTYPE_TAPS; +int AudioSRC::createRationalFilter(int upFactor, int downFactor, float gain, Quality quality) { + + int prototypeTaps = prototypeFilterTable[quality].taps; + int prototypeCoefs = prototypeFilterTable[quality].coefs; + const float* prototypeFilter = prototypeFilterTable[quality].filter; + + int numTaps = prototypeTaps; int numPhases = upFactor; int numCoefs = numTaps * numPhases; - int oldCoefs = numCoefs; - int prototypeCoefs = PROTOTYPE_TAPS * PROTOTYPE_PHASES; // // When downsampling, we can lower the filter cutoff by downFactor/upFactor using the // time-scaling property of the Fourier transform. The gain is adjusted accordingly. // if (downFactor > upFactor) { + int oldCoefs = numCoefs; numCoefs = ((int64_t)oldCoefs * downFactor) / upFactor; numTaps = (numCoefs + upFactor - 1) / upFactor; gain *= (float)oldCoefs / numCoefs; @@ -149,18 +153,22 @@ int AudioSRC::createRationalFilter(int upFactor, int downFactor, float gain) { return numTaps; } -int AudioSRC::createIrrationalFilter(int upFactor, int downFactor, float gain) { - int numTaps = PROTOTYPE_TAPS; +int AudioSRC::createIrrationalFilter(int upFactor, int downFactor, float gain, Quality quality) { + + int prototypeTaps = prototypeFilterTable[quality].taps; + int prototypeCoefs = prototypeFilterTable[quality].coefs; + const float* prototypeFilter = prototypeFilterTable[quality].filter; + + int numTaps = prototypeTaps; int numPhases = upFactor; int numCoefs = numTaps * numPhases; - int oldCoefs = numCoefs; - int prototypeCoefs = PROTOTYPE_TAPS * PROTOTYPE_PHASES; // // When downsampling, we can lower the filter cutoff by downFactor/upFactor using the // time-scaling property of the Fourier transform. The gain is adjusted accordingly. // if (downFactor > upFactor) { + int oldCoefs = numCoefs; numCoefs = ((int64_t)oldCoefs * downFactor) / upFactor; numTaps = (numCoefs + upFactor - 1) / upFactor; gain *= (float)oldCoefs / numCoefs; @@ -1405,7 +1413,8 @@ int AudioSRC::render(float** inputs, float** outputs, int inputFrames) { return outputFrames; } -AudioSRC::AudioSRC(int inputSampleRate, int outputSampleRate, int numChannels) { +AudioSRC::AudioSRC(int inputSampleRate, int outputSampleRate, int numChannels, Quality quality) { + assert(inputSampleRate > 0); assert(outputSampleRate > 0); assert(numChannels > 0); @@ -1433,9 +1442,9 @@ AudioSRC::AudioSRC(int inputSampleRate, int outputSampleRate, int numChannels) { // create the polyphase filter if (_step == 0) { - _numTaps = createRationalFilter(_upFactor, _downFactor, 1.0f); + _numTaps = createRationalFilter(_upFactor, _downFactor, 1.0f, quality); } else { - _numTaps = createIrrationalFilter(_upFactor, _downFactor, 1.0f); + _numTaps = createIrrationalFilter(_upFactor, _downFactor, 1.0f, quality); } //printf("up=%d down=%.3f taps=%d\n", _upFactor, _downFactor + (LO32(_step)< Date: Mon, 26 Dec 2016 20:16:05 -0800 Subject: [PATCH 094/118] quiet some warnings --- plugins/steamClient/src/SteamAPIPlugin.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/steamClient/src/SteamAPIPlugin.h b/plugins/steamClient/src/SteamAPIPlugin.h index 90fda06de3..0305a2d23f 100644 --- a/plugins/steamClient/src/SteamAPIPlugin.h +++ b/plugins/steamClient/src/SteamAPIPlugin.h @@ -19,19 +19,19 @@ class QUrl; class SteamAPIPlugin : public SteamClientPlugin { public: - bool isRunning(); + bool isRunning() override; - bool init(); - void shutdown(); + bool init() override; + void shutdown() override; - void runCallbacks(); + void runCallbacks() override; - void requestTicket(TicketRequestCallback callback); - void updateLocation(QString status, QUrl locationUrl); - void openInviteOverlay(); - void joinLobby(QString lobbyId); + void requestTicket(TicketRequestCallback callback) override; + void updateLocation(QString status, QUrl locationUrl) override; + void openInviteOverlay() override; + void joinLobby(QString lobbyId) override; - int getSteamVRBuildID(); + int getSteamVRBuildID() override; }; #endif // hifi_SteamAPIPlugin_h From e674be9c8c940a91cad01af506fbf22a5000d8d9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 27 Dec 2016 15:15:11 -0800 Subject: [PATCH 095/118] fix animated entities --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6066905943..0e14beab87 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -924,8 +924,8 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { for (auto& trigger : triggersOut) { _animVars.setTrigger(trigger); } - applyOverridePoses(); } + applyOverridePoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); // copy internal poses to external poses From 901c020aaeaef8b471c5149b4da28398561639e5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Dec 2016 13:57:51 -0800 Subject: [PATCH 096/118] Optimizations, SIMD and const correctness --- libraries/render-utils/src/Model.cpp | 11 ++++++-- libraries/render/src/render/Scene.cpp | 4 +-- libraries/render/src/render/Scene.h | 2 +- libraries/shared/src/AABox.cpp | 10 +++---- libraries/shared/src/GLMHelpers.cpp | 5 +++- tests/shared/src/GLMHelpersTests.cpp | 38 +++++++++++++++++++++++++++ tests/shared/src/GLMHelpersTests.h | 1 + 7 files changed, 60 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 436574a1ff..ffbe1fb34c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1160,7 +1160,8 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + static const glm::mat4 zeroScale( + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); @@ -1170,11 +1171,17 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 temp, out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&modelToWorld, (glm_vec4*)&jointMatrix, (glm_vec4*)&temp); + glm_mat4_mul((glm_vec4*)&temp, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; +#endif // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index f918fc0bf6..95fef3e9f0 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -35,7 +35,7 @@ void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor) _updateFunctors.push_back(functor); } -void PendingChanges::merge(PendingChanges& changes) { +void PendingChanges::merge(const PendingChanges& changes) { _resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end()); _resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end()); _removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end()); @@ -71,7 +71,7 @@ void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) { void consolidateChangeQueue(PendingChangesQueue& queue, PendingChanges& singleBatch) { while (!queue.empty()) { - auto pendingChanges = queue.front(); + const auto& pendingChanges = queue.front(); singleBatch.merge(pendingChanges); queue.pop(); }; diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 6b57a22a36..13475d0556 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -34,7 +34,7 @@ public: void updateItem(ItemID id, const UpdateFunctorPointer& functor); void updateItem(ItemID id) { updateItem(id, nullptr); } - void merge(PendingChanges& changes); + void merge(const PendingChanges& changes); ItemIDs _resetItems; Payloads _resetPayloads; diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 5e9c031355..4a74fb4033 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -575,18 +575,18 @@ void AABox::transform(const Transform& transform) { // Logic based on http://clb.demon.fi/MathGeoLib/nightly/docs/AABB.cpp_code.html#471 void AABox::transform(const glm::mat4& matrix) { + // FIXME use simd operations auto halfSize = _scale * 0.5f; auto center = _corner + halfSize; halfSize = abs(halfSize); - auto newCenter = transformPoint(matrix, center); - auto mm = glm::transpose(glm::mat3(matrix)); vec3 newDir = vec3( - glm::dot(glm::abs(vec3(mm[0])), halfSize), - glm::dot(glm::abs(vec3(mm[1])), halfSize), - glm::dot(glm::abs(vec3(mm[2])), halfSize) + glm::dot(glm::abs(mm[0]), halfSize), + glm::dot(glm::abs(mm[1]), halfSize), + glm::dot(glm::abs(mm[2]), halfSize) ); + auto newCenter = transformPoint(matrix, center); _corner = newCenter - newDir; _scale = newDir * 2.0f; } diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 3520a4dc44..6aa4f68f22 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -502,7 +502,10 @@ glm::mat4 cancelOutRollAndPitch(const glm::mat4& m) { glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) { glm::vec4 temp = m * glm::vec4(p, 1.0f); - return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w); + if (temp.w != 1.0f) { + temp *= (1.0f / temp.w); + } + return glm::vec3(temp); } // does not handle non-uniform scale correctly, but it's faster then transformVectorFull diff --git a/tests/shared/src/GLMHelpersTests.cpp b/tests/shared/src/GLMHelpersTests.cpp index a796d62ba5..8d26d35c69 100644 --- a/tests/shared/src/GLMHelpersTests.cpp +++ b/tests/shared/src/GLMHelpersTests.cpp @@ -15,6 +15,8 @@ #include #include <../QTestExtensions.h> +#include +#include QTEST_MAIN(GLMHelpersTests) @@ -102,3 +104,39 @@ void GLMHelpersTests::testSixByteOrientationCompression() { testQuatCompression(-(ROT_Y_180 * ROT_Z_30 * ROT_X_90)); testQuatCompression(-(ROT_Z_30 * ROT_X_90 * ROT_Y_180)); } + +#define LOOPS 500000 + +void GLMHelpersTests::testSimd() { + glm::mat4 a = glm::translate(glm::mat4(), vec3(1, 4, 9)); + glm::mat4 b = glm::rotate(glm::mat4(), PI / 3, vec3(0, 1, 0)); + glm::mat4 a1, b1; + glm::mat4 a2, b2; + + a1 = a * b; + b1 = b * a; + glm_mat4_mul((glm_vec4*)&a, (glm_vec4*)&b, (glm_vec4*)&a2); + glm_mat4_mul((glm_vec4*)&b, (glm_vec4*)&a, (glm_vec4*)&b2); + + + { + QElapsedTimer timer; + timer.start(); + for (size_t i = 0; i < LOOPS; ++i) { + a1 = a * b; + b1 = b * a; + } + qDebug() << "Native " << timer.elapsed(); + } + + { + QElapsedTimer timer; + timer.start(); + for (size_t i = 0; i < LOOPS; ++i) { + glm_mat4_mul((glm_vec4*)&a, (glm_vec4*)&b, (glm_vec4*)&a2); + glm_mat4_mul((glm_vec4*)&b, (glm_vec4*)&a, (glm_vec4*)&b2); + } + qDebug() << "SIMD " << timer.elapsed(); + } + qDebug() << "Done "; +} diff --git a/tests/shared/src/GLMHelpersTests.h b/tests/shared/src/GLMHelpersTests.h index 40d552a07b..acc7b533f5 100644 --- a/tests/shared/src/GLMHelpersTests.h +++ b/tests/shared/src/GLMHelpersTests.h @@ -20,6 +20,7 @@ class GLMHelpersTests : public QObject { private slots: void testEulerDecomposition(); void testSixByteOrientationCompression(); + void testSimd(); }; float getErrorDifference(const float& a, const float& b); From 0bb82d2b73614b31fa703ad0e5ec45fcfecd5f01 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 28 Dec 2016 16:04:22 -0500 Subject: [PATCH 097/118] use libquazip5.so in debug --- cmake/externals/quazip/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index f00403640a..b8b3fe43d8 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -38,13 +38,13 @@ set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location if (APPLE) set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library") elseif (WIN32) set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library") else () set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library") endif () include(SelectLibraryConfigurations) @@ -52,4 +52,4 @@ select_library_configurations(${EXTERNAL_NAME_UPPER}) # Force selected libraries into the cache set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries") -set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") \ No newline at end of file +set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries") From f79c6e08f5c40aa0d8c07cd428a6325760fe3e62 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Dec 2016 14:18:23 -0800 Subject: [PATCH 098/118] Track dropped frames as reported by the Oculus SDK --- interface/src/Application.cpp | 1 + libraries/plugins/src/plugins/DisplayPlugin.h | 4 ++++ plugins/oculus/src/OculusDisplayPlugin.cpp | 14 +++++++++++--- plugins/oculus/src/OculusDisplayPlugin.h | 4 ++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d92ba53f6e..eb2f6f5896 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1271,6 +1271,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["sim_rate"] = getAverageSimsPerSecond(); properties["avatar_sim_rate"] = getAvatarSimrate(); properties["has_async_reprojection"] = displayPlugin->hasAsyncReprojection(); + properties["hardware_stats"] = displayPlugin->getHardwareStats(); auto bandwidthRecorder = DependencyManager::get(); properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(); diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 6b55d8ed64..2491aed817 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -194,6 +195,9 @@ public: virtual float newFramePresentRate() const { return -1.0f; } // Rate at which rendered frames are being skipped virtual float droppedFrameRate() const { return -1.0f; } + + // Hardware specific stats + virtual QJsonObject getHardwareStats() const { return QJsonObject(); } uint32_t presentCount() const { return _presentedFrameIndex; } // Time since last call to incrementPresentCount (only valid if DEBUG_PAINT_DELAY is defined) diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 88ca02edc2..7a56124298 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -147,19 +147,27 @@ void OculusDisplayPlugin::hmdPresent() { logWarning("Failed to present"); } - static int droppedFrames = 0; + static int compositorDroppedFrames = 0; + static int appDroppedFrames = 0; ovrPerfStats perfStats; ovr_GetPerfStats(_session, &perfStats); for (int i = 0; i < perfStats.FrameStatsCount; ++i) { const auto& frameStats = perfStats.FrameStats[i]; - int delta = frameStats.CompositorDroppedFrameCount - droppedFrames; + int delta = frameStats.CompositorDroppedFrameCount - compositorDroppedFrames; _stutterRate.increment(delta); - droppedFrames = frameStats.CompositorDroppedFrameCount; + compositorDroppedFrames = frameStats.CompositorDroppedFrameCount; + appDroppedFrames = frameStats.AppDroppedFrameCount; } + _hardwareStats["app_dropped_frame_count"] = appDroppedFrames; } _presentRate.increment(); } + +QJsonObject OculusDisplayPlugin::getHardwareStats() const { + return _hardwareStats; +} + bool OculusDisplayPlugin::isHmdMounted() const { ovrSessionStatus status; return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) && diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index fce8e9e6ce..a953e01e2f 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -19,6 +19,8 @@ public: QString getPreferredAudioInDevice() const override; QString getPreferredAudioOutDevice() const override; + + virtual QJsonObject getHardwareStats() const; protected: bool internalActivate() override; @@ -33,5 +35,7 @@ private: ovrTextureSwapChain _textureSwapChain; gpu::FramebufferPointer _outputFramebuffer; bool _customized { false }; + + QJsonObject _hardwareStats; }; From 522403cf48390343b7df40cfcdbce8fec3d31514 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:08:17 -0800 Subject: [PATCH 099/118] fix build error on linux --- tests/shared/src/TraceTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/shared/src/TraceTests.cpp b/tests/shared/src/TraceTests.cpp index 5536d17ca6..a3ef2e8725 100644 --- a/tests/shared/src/TraceTests.cpp +++ b/tests/shared/src/TraceTests.cpp @@ -28,7 +28,7 @@ void TraceTests::testTraceSerialization() { auto start = usecTimestampNow(); PROFILE_RANGE(test, "TestEvent") for (size_t i = 0; i < 10000; ++i) { - SAMPLE_PROFILE_COUNTER(0.1f, test, "TestCounter", { { "i", i } }) + SAMPLE_PROFILE_COUNTER(0.1f, test, "TestCounter", { { "i", (int)i } }) } auto duration = usecTimestampNow() - start; duration /= USECS_PER_MSEC; From 9763566bdb73c7cf63d4ca7c4630c1747e295849 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:09:31 -0800 Subject: [PATCH 100/118] fix bitrot from April 2016 --- tests/shared/src/MovingPercentileTests.cpp | 34 ++++++++++++---------- tests/shared/src/MovingPercentileTests.h | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/tests/shared/src/MovingPercentileTests.cpp b/tests/shared/src/MovingPercentileTests.cpp index fbbc3c7b9e..741c966525 100644 --- a/tests/shared/src/MovingPercentileTests.cpp +++ b/tests/shared/src/MovingPercentileTests.cpp @@ -39,19 +39,21 @@ void MovingPercentileTests::testRunningMedian() { } -float MovingPercentileTests::random() { - return rand() / (float)RAND_MAX; +int64_t MovingPercentileTests::random() { + return ((int64_t) rand() << 48) ^ + ((int64_t) rand() << 32) ^ + ((int64_t) rand() << 16) ^ + ((int64_t) rand()); } void MovingPercentileTests::testRunningMinForN (int n) { - // Stores the last n samples - QQueue samples; + QQueue samples; MovingPercentile movingMin (n, 0.0f); for (int s = 0; s < 3 * n; ++s) { - float sample = random(); + int64_t sample = random(); samples.push_back(sample); if (samples.size() > n) @@ -64,30 +66,32 @@ void MovingPercentileTests::testRunningMinForN (int n) { movingMin.updatePercentile(sample); // Calculate the minimum of the moving samples - float expectedMin = std::numeric_limits::max(); + int64_t expectedMin = std::numeric_limits::max(); int prevSize = samples.size(); - for (auto val : samples) + for (auto val : samples) { expectedMin = std::min(val, expectedMin); + } QCOMPARE(samples.size(), prevSize); - QCOMPARE(movingMin.getValueAtPercentile(), expectedMin); + QVERIFY(movingMin.getValueAtPercentile() - expectedMin == 0L); } } void MovingPercentileTests::testRunningMaxForN (int n) { // Stores the last n samples - QQueue samples; + QQueue samples; MovingPercentile movingMax (n, 1.0f); for (int s = 0; s < 10000; ++s) { - float sample = random(); + int64_t sample = random(); samples.push_back(sample); - if (samples.size() > n) + if (samples.size() > n) { samples.pop_front(); + } if (samples.size() == 0) { QFAIL_WITH_MESSAGE("\n\n\n\tWTF\n\tsamples.size() = " << samples.size() << ", n = " << n); @@ -96,22 +100,22 @@ void MovingPercentileTests::testRunningMaxForN (int n) { movingMax.updatePercentile(sample); // Calculate the maximum of the moving samples - float expectedMax = std::numeric_limits::min(); + int64_t expectedMax = std::numeric_limits::min(); for (auto val : samples) expectedMax = std::max(val, expectedMax); - QCOMPARE(movingMax.getValueAtPercentile(), expectedMax); + QVERIFY(movingMax.getValueAtPercentile() - expectedMax == 0L); } } void MovingPercentileTests::testRunningMedianForN (int n) { // Stores the last n samples - QQueue samples; + QQueue samples; MovingPercentile movingMedian (n, 0.5f); for (int s = 0; s < 10000; ++s) { - float sample = random(); + int64_t sample = random(); samples.push_back(sample); if (samples.size() > n) diff --git a/tests/shared/src/MovingPercentileTests.h b/tests/shared/src/MovingPercentileTests.h index ffc8ddb0f6..d9c96d2752 100644 --- a/tests/shared/src/MovingPercentileTests.h +++ b/tests/shared/src/MovingPercentileTests.h @@ -25,7 +25,7 @@ private slots: private: // Utilities and helper functions - float random(); + int64_t random(); void testRunningMinForN (int n); void testRunningMaxForN (int n); void testRunningMedianForN (int n); From 39538db63afd75a0c2562740dc726cd640e61cb5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:10:45 -0800 Subject: [PATCH 101/118] fix bitrot from changed glm::vec3 API? --- tests/shared/src/GeometryUtilTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp index 79472a1128..ccb3dc8a0e 100644 --- a/tests/shared/src/GeometryUtilTests.cpp +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -255,11 +255,11 @@ void GeometryUtilTests::testTwistSwingDecomposition() { glm::quat measuredTwistRotation; glm::quat measuredSwingRotation; swingTwistDecomposition(totalRotation, twistAxis, measuredSwingRotation, measuredTwistRotation); - + // dot decomposed with components float twistDot = fabsf(glm::dot(twistRotation, measuredTwistRotation)); float swingDot = fabsf(glm::dot(swingRotation, measuredSwingRotation)); - + // the dot products should be very close to 1.0 const float MIN_ERROR = 1.0e-6f; QCOMPARE_WITH_ABS_ERROR(1.0f, twistDot, MIN_ERROR); @@ -277,7 +277,7 @@ void GeometryUtilTests::testSphereCapsulePenetration() { glm::vec3 capsuleEnd(0.0f, 10.0f, 0.0f); float capsuleRadius = 1.0f; - glm::vec3 penetration(glm::vec3::_null); + glm::vec3 penetration(0.0f); bool hit = findSphereCapsulePenetration(sphereCenter, sphereRadius, capsuleStart, capsuleEnd, capsuleRadius, penetration); QCOMPARE(hit, true); QCOMPARE_WITH_ABS_ERROR(penetration, glm::vec3(-0.5f, 0.0f, 0.0f), EPSILON); From 96a9ef8f44dde9669ae17fa3db354062b1e25d6d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:11:54 -0800 Subject: [PATCH 102/118] fix bitrot after UDT overhaul --- tests/networking/src/PacketTests.cpp | 30 +++++++++++------------ tests/networking/src/ResourceTests.cpp | 33 +++++++------------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/tests/networking/src/PacketTests.cpp b/tests/networking/src/PacketTests.cpp index cbb949aa84..ff2149930e 100644 --- a/tests/networking/src/PacketTests.cpp +++ b/tests/networking/src/PacketTests.cpp @@ -12,29 +12,29 @@ #include "PacketTests.h" #include "../QTestExtensions.h" -#include +#include QTEST_MAIN(PacketTests) -std::unique_ptr copyToReadPacket(std::unique_ptr& packet) { +std::unique_ptr copyToReadPacket(std::unique_ptr& packet) { auto size = packet->getDataSize(); auto data = std::unique_ptr(new char[size]); memcpy(data.get(), packet->getData(), size); - return Packet::fromReceivedPacket(std::move(data), size, HifiSockAddr()); + return NLPacket::fromReceivedPacket(std::move(data), size, HifiSockAddr()); } void PacketTests::emptyPacketTest() { - auto packet = Packet::create(PacketType::Unknown); + auto packet = NLPacket::create(PacketType::Unknown); QCOMPARE(packet->getType(), PacketType::Unknown); QCOMPARE(packet->getPayloadSize(), 0); - QCOMPARE(packet->getDataSize(), packet->totalHeadersSize()); + QCOMPARE(packet->getDataSize(), NLPacket::totalHeaderSize(packet->getType())); QCOMPARE(packet->bytesLeftToRead(), 0); QCOMPARE(packet->bytesAvailableForWrite(), packet->getPayloadCapacity()); } void PacketTests::packetTypeTest() { - auto packet = Packet::create(PacketType::EntityAdd); + auto packet = NLPacket::create(PacketType::EntityAdd); QCOMPARE(packet->getType(), PacketType::EntityAdd); @@ -46,7 +46,7 @@ void PacketTests::packetTypeTest() { } void PacketTests::writeTest() { - auto packet = Packet::create(PacketType::Unknown); + auto packet = NLPacket::create(PacketType::Unknown); QCOMPARE(packet->getPayloadSize(), 0); @@ -62,7 +62,7 @@ void PacketTests::writeTest() { void PacketTests::readTest() { // Test reads for several different size packets for (int i = 1; i < 4; i++) { - auto packet = Packet::create(PacketType::Unknown); + auto packet = NLPacket::create(PacketType::Unknown); auto size = packet->getPayloadCapacity(); size /= i; @@ -91,7 +91,7 @@ void PacketTests::readTest() { } void PacketTests::writePastCapacityTest() { - auto packet = Packet::create(PacketType::Unknown); + auto packet = NLPacket::create(PacketType::Unknown); auto size = packet->getPayloadCapacity(); char* data = new char[size]; @@ -111,20 +111,20 @@ void PacketTests::writePastCapacityTest() { QCOMPARE(packet->bytesAvailableForWrite(), 0); QCOMPARE(packet->getPayloadSize(), size); - QCOMPARE(Packet::PACKET_WRITE_ERROR, packet->write("data")); - - // Packet::write() shouldn't allow the caller to write if no space is left + QCOMPARE(NLPacket::PACKET_WRITE_ERROR, packet->write("data")); // asserts in DEBUG + + // NLPacket::write() shouldn't allow the caller to write if no space is left QCOMPARE(packet->getPayloadSize(), size); } void PacketTests::primitiveTest() { - auto packet = Packet::create(PacketType::Unknown); + auto packet = NLPacket::create(PacketType::Unknown); int value1 = 5; char value2 = 10; bool value3 = true; qint64 value4 = -93404; - + packet->writePrimitive(value1); packet->writePrimitive(value2); packet->writePrimitive(value3); @@ -133,7 +133,7 @@ void PacketTests::primitiveTest() { auto recvPacket = copyToReadPacket(packet); // Peek & read first value - { + { int peekValue = 0; QCOMPARE(recvPacket->peekPrimitive(&peekValue), (int)sizeof(peekValue)); QCOMPARE(peekValue, value1); diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp index e6dcf18230..a96abc1b0c 100644 --- a/tests/networking/src/ResourceTests.cpp +++ b/tests/networking/src/ResourceTests.cpp @@ -37,25 +37,10 @@ void ResourceTests::initTestCase() { static QSharedPointer resource; -static bool waitForSignal(QObject *sender, const char *signal, int timeout = 1000) { - QEventLoop loop; - QTimer timer; - timer.setInterval(timeout); - timer.setSingleShot(true); - - loop.connect(sender, signal, SLOT(quit())); - loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); - timer.start(); - loop.exec(); - - return timer.isActive(); -} - void ResourceTests::downloadFirst() { - // download the Mery fst file QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); - resource = QSharedPointer::create(meryUrl, false); + resource = QSharedPointer::create(meryUrl); resource->setSelf(resource); const int timeout = 1000; @@ -64,9 +49,9 @@ void ResourceTests::downloadFirst() { timer.setInterval(timeout); timer.setSingleShot(true); - loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); - loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); - loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + connect(resource.data(), &Resource::loaded, &loop, &QEventLoop::quit); + connect(resource.data(), &Resource::failed, &loop, &QEventLoop::quit); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(); resource->ensureLoading(); @@ -76,10 +61,9 @@ void ResourceTests::downloadFirst() { } void ResourceTests::downloadAgain() { - // download the Mery fst file QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); - resource = QSharedPointer::create(meryUrl, false); + resource = QSharedPointer::create(meryUrl); resource->setSelf(resource); const int timeout = 1000; @@ -88,14 +72,13 @@ void ResourceTests::downloadAgain() { timer.setInterval(timeout); timer.setSingleShot(true); - loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); - loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); - loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + connect(resource.data(), &Resource::loaded, &loop, &QEventLoop::quit); + connect(resource.data(), &Resource::failed, &loop, &QEventLoop::quit); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.start(); resource->ensureLoading(); loop.exec(); QVERIFY(resource->isLoaded()); - } From 1e38170ba9a17d498c1a32ac4526e466a77cc897 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:12:22 -0800 Subject: [PATCH 103/118] fix bitrot from AudioRingBuffer API change --- tests/audio/src/AudioRingBufferTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/audio/src/AudioRingBufferTests.cpp b/tests/audio/src/AudioRingBufferTests.cpp index ea384cad61..7021197792 100644 --- a/tests/audio/src/AudioRingBufferTests.cpp +++ b/tests/audio/src/AudioRingBufferTests.cpp @@ -36,7 +36,7 @@ void AudioRingBufferTests::runAllTests() { int readIndexAt; - AudioRingBuffer ringBuffer(10, false, 10); // makes buffer of 100 int16_t samples + AudioRingBuffer ringBuffer(10, 10); // makes buffer of 100 int16_t samples for (int T = 0; T < 300; T++) { writeIndexAt = 0; From be0ca41eb3206b61b93f7ef62935c8012aed06b8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Dec 2016 18:27:34 -0800 Subject: [PATCH 104/118] make implicit cast explicit --- libraries/shared/src/GLMHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 6aa4f68f22..ec244553f8 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -370,7 +370,7 @@ glm::quat glmExtractRotation(const glm::mat4& matrix) { glm::vec3 extractScale(const glm::mat4& matrix) { glm::mat3 m(matrix); float det = glm::determinant(m); - if (det < 0) { + if (det < 0.0f) { // left handed matrix, flip sign to compensate. return glm::vec3(-glm::length(m[0]), glm::length(m[1]), glm::length(m[2])); } else { From a72f60152befa0c6ecf4841b106d21b420ea0d5b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 08:53:37 -0800 Subject: [PATCH 105/118] audit use of QTimer intervals to make sure we're using Qt::PreciseTimer when appropriate --- interface/src/Application.cpp | 12 ++++++------ interface/src/ui/DiskCacheEditor.cpp | 2 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 3 ++- libraries/networking/src/DomainHandler.cpp | 4 ++-- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/NodeList.cpp | 2 +- libraries/networking/src/ThreadedAssignment.cpp | 4 ++-- libraries/script-engine/src/ScriptEngine.cpp | 6 ++++++ libraries/shared/src/SettingManager.cpp | 2 +- plugins/openvr/src/OpenVrHelpers.cpp | 2 +- tests/gpu-test/src/TestWindow.cpp | 1 + tests/networking/src/ResourceTests.cpp | 10 ++++++++-- tests/render-perf/src/main.cpp | 2 +- tests/render-texture-load/src/main.cpp | 2 +- tests/render-utils/src/main.cpp | 2 +- 15 files changed, 35 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d92ba53f6e..835355249a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1135,7 +1135,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_settingsThread, SIGNAL(finished()), &_settingsTimer, SLOT(stop())); _settingsTimer.moveToThread(&_settingsThread); _settingsTimer.setSingleShot(false); - _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); + _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable _settingsThread.start(); if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { @@ -1240,7 +1240,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Periodically send fps as a user activity event QTimer* sendStatsTimer = new QTimer(this); - sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS); + sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS); // 10s, Qt::CoarseTimer acceptable connect(sendStatsTimer, &QTimer::timeout, this, [this]() { QJsonObject properties = {}; @@ -1341,7 +1341,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Periodically check for count of nearby avatars static int lastCountOfNearbyAvatars = -1; QTimer* checkNearbyAvatarsTimer = new QTimer(this); - checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); + checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); // 10 seconds, Qt::CoarseTimer ok connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() { auto avatarManager = DependencyManager::get(); int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getPosition(), @@ -1499,16 +1499,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing. static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000; - _addAssetToWorldResizeTimer.setInterval(ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS); + _addAssetToWorldResizeTimer.setInterval(ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS); // 1s, Qt::CoarseTimer acceptable connect(&_addAssetToWorldResizeTimer, &QTimer::timeout, this, &Application::addAssetToWorldCheckModelSize); // Auto-update and close adding asset to world info message box. static const int ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS = 5000; - _addAssetToWorldInfoTimer.setInterval(ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS); + _addAssetToWorldInfoTimer.setInterval(ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS); // 5s, Qt::CoarseTimer acceptable _addAssetToWorldInfoTimer.setSingleShot(true); connect(&_addAssetToWorldInfoTimer, &QTimer::timeout, this, &Application::addAssetToWorldInfoTimeout); static const int ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS = 8000; - _addAssetToWorldErrorTimer.setInterval(ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS); + _addAssetToWorldErrorTimer.setInterval(ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS); // 8s, Qt::CoarseTimer acceptable _addAssetToWorldErrorTimer.setSingleShot(true); connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout); diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp index ed85b87d63..c7f4bfe39b 100644 --- a/interface/src/ui/DiskCacheEditor.cpp +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -91,7 +91,7 @@ void DiskCacheEditor::makeDialog() { static const int REFRESH_INTERVAL = 100; // msec _refreshTimer = new QTimer(_dialog); - _refreshTimer->setInterval(REFRESH_INTERVAL); + _refreshTimer->setInterval(REFRESH_INTERVAL); // 100ms, Qt::CoarseTimer acceptable _refreshTimer->setSingleShot(false); QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh); _refreshTimer->start(); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 3a803172d0..8559872aba 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -426,7 +426,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // a timer with a small interval is used to get better performance. QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); - _updateTimer.setInterval(MIN_TIMER_MS); + _updateTimer.setTimerType(Qt::PreciseTimer); + _updateTimer.setInterval(MIN_TIMER_MS); // 5ms, Qt::PreciseTimer required _updateTimer.start(); auto rootContext = getRootContext(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index c9106c7162..3c20c3798f 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -41,12 +41,12 @@ DomainHandler::DomainHandler(QObject* parent) : // setup a timeout for failure on settings requests static const int DOMAIN_SETTINGS_TIMEOUT_MS = 5000; - _settingsTimer.setInterval(DOMAIN_SETTINGS_TIMEOUT_MS); + _settingsTimer.setInterval(DOMAIN_SETTINGS_TIMEOUT_MS); // 5s, Qt::CoarseTimer acceptable connect(&_settingsTimer, &QTimer::timeout, this, &DomainHandler::settingsReceiveFail); // setup the API refresh timer for auto connection information refresh from API when failing to connect const int API_REFRESH_TIMEOUT_MSEC = 2500; - _apiRefreshTimer.setInterval(API_REFRESH_TIMEOUT_MSEC); + _apiRefreshTimer.setInterval(API_REFRESH_TIMEOUT_MSEC); // 2.5s, Qt::CoarseTimer acceptable auto addressManager = DependencyManager::get(); connect(&_apiRefreshTimer, &QTimer::timeout, addressManager.data(), &AddressManager::refreshPreviousLookup); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index a5cf1d9527..b9bf9fea67 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -902,7 +902,7 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { connect(_initialSTUNTimer.data(), &QTimer::timeout, this, &LimitedNodeList::sendSTUNRequest); const int STUN_INITIAL_UPDATE_INTERVAL_MSECS = 250; - _initialSTUNTimer->setInterval(STUN_INITIAL_UPDATE_INTERVAL_MSECS); + _initialSTUNTimer->setInterval(STUN_INITIAL_UPDATE_INTERVAL_MSECS); // 250ms, Qt::CoarseTimer acceptable // if we don't know the STUN IP yet we need to wait until it is known to start STUN requests if (_stunSockAddr.getAddress().isNull()) { diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 27b3e11fda..0f18dd1b55 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -102,7 +102,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode); // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) - _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); + _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); // 1s, Qt::CoarseTimer acceptable connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index e6e3c17db7..c7b6b05c35 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -27,11 +27,11 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : _statsTimer(this) { static const int STATS_TIMEOUT_MS = 1000; - _statsTimer.setInterval(STATS_TIMEOUT_MS); + _statsTimer.setInterval(STATS_TIMEOUT_MS); // 1s, Qt::CoarseTimer acceptable connect(&_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket); connect(&_domainServerTimer, &QTimer::timeout, this, &ThreadedAssignment::checkInWithDomainServerOrExit); - _domainServerTimer.setInterval(DOMAIN_SERVER_CHECK_IN_MSECS); + _domainServerTimer.setInterval(DOMAIN_SERVER_CHECK_IN_MSECS); // 1s, Qt::CoarseTimer acceptable // if the NL tells us we got a DS response, clear our member variable of queued check-ins auto nodeList = DependencyManager::get(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6c3f6c7fa8..45d37aa52e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1087,6 +1087,12 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int QTimer* newTimer = new QTimer(this); newTimer->setSingleShot(isSingleShot); + // The default timer type is not very accurate below about 200ms http://doc.qt.io/qt-5/qt.html#TimerType-enum + static const int MIN_TIMEOUT_FOR_COARSE_TIMER = 200; + if (intervalMS < MIN_TIMEOUT_FOR_COARSE_TIMER) { + newTimer->setTimerType(Qt::PreciseTimer); + } + connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); // make sure the timer stops when the script does diff --git a/libraries/shared/src/SettingManager.cpp b/libraries/shared/src/SettingManager.cpp index 929fe2b0ff..6c246d4cea 100644 --- a/libraries/shared/src/SettingManager.cpp +++ b/libraries/shared/src/SettingManager.cpp @@ -83,7 +83,7 @@ namespace Setting { _saveTimer = new QTimer(this); Q_CHECK_PTR(_saveTimer); _saveTimer->setSingleShot(true); // We will restart it once settings are saved. - _saveTimer->setInterval(SAVE_INTERVAL_MSEC); + _saveTimer->setInterval(SAVE_INTERVAL_MSEC); // 5s, Qt::CoarseTimer acceptable connect(_saveTimer, SIGNAL(timeout()), this, SLOT(saveAll())); } _saveTimer->start(); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 09713e9f57..29ef640bf3 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -363,7 +363,7 @@ void showMinSpecWarning() { vrOverlay->ShowOverlay(minSpecFailedOverlay); QTimer* timer = new QTimer(&miniApp); - timer->setInterval(FAILED_MIN_SPEC_UPDATE_INTERVAL_MS); + timer->setInterval(FAILED_MIN_SPEC_UPDATE_INTERVAL_MS); // Qt::CoarseTimer acceptable, we don't need this to be frame rate accurate QObject::connect(timer, &QTimer::timeout, [&] { vr::TrackedDevicePose_t vrPoses[vr::k_unMaxTrackedDeviceCount]; vrSystem->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseSeated, 0, vrPoses, vr::k_unMaxTrackedDeviceCount); diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp index a9f5216991..0ca1ad2297 100644 --- a/tests/gpu-test/src/TestWindow.cpp +++ b/tests/gpu-test/src/TestWindow.cpp @@ -34,6 +34,7 @@ TestWindow::TestWindow() { auto timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); timer->setInterval(5); connect(timer, &QTimer::timeout, [&] { draw(); }); timer->start(); diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp index e6dcf18230..f9ff5887b6 100644 --- a/tests/networking/src/ResourceTests.cpp +++ b/tests/networking/src/ResourceTests.cpp @@ -40,6 +40,12 @@ static QSharedPointer resource; static bool waitForSignal(QObject *sender, const char *signal, int timeout = 1000) { QEventLoop loop; QTimer timer; + + // The default timer type is not very accurate below about 200ms http://doc.qt.io/qt-5/qt.html#TimerType-enum + static const int MIN_TIMEOUT_FOR_COARSE_TIMER = 200; + if (timeout < MIN_TIMEOUT_FOR_COARSE_TIMER) { + timer.setTimerType(Qt::PreciseTimer); + } timer.setInterval(timeout); timer.setSingleShot(true); @@ -61,7 +67,7 @@ void ResourceTests::downloadFirst() { const int timeout = 1000; QEventLoop loop; QTimer timer; - timer.setInterval(timeout); + timer.setInterval(timeout); // 1s, Qt::CoarseTimer acceptable timer.setSingleShot(true); loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); @@ -85,7 +91,7 @@ void ResourceTests::downloadAgain() { const int timeout = 1000; QEventLoop loop; QTimer timer; - timer.setInterval(timeout); + timer.setInterval(timeout); // 1s, Qt::CoarseTimer acceptable timer.setSingleShot(true); loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 31bb13f4d0..df2c552d1a 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -546,7 +546,7 @@ public: restorePosition(); QTimer* timer = new QTimer(this); - timer->setInterval(0); + timer->setInterval(0); // Qt::CoarseTimer acceptable connect(timer, &QTimer::timeout, this, [this] { draw(); }); diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 68f007ce70..09a420f018 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -355,7 +355,7 @@ public: } QTimer* timer = new QTimer(this); - timer->setInterval(0); + timer->setInterval(0); // Qt::CoarseTimer acceptable connect(timer, &QTimer::timeout, this, [this] { draw(); }); diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 7e909bf31f..741fdbdddd 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -201,7 +201,7 @@ int main(int argc, char** argv) { QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow window; QTimer timer; - timer.setInterval(1); + timer.setInterval(1); // Qt::CoarseTimer acceptable app.connect(&timer, &QTimer::timeout, &app, [&] { window.draw(); }); From f46b8f3e6df4041426b4340b31647f67987462cc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Dec 2016 10:17:40 -0800 Subject: [PATCH 106/118] Track compositor dropped frames as well. --- plugins/oculus/src/OculusDisplayPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 7a56124298..bad346b1ca 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -159,6 +159,7 @@ void OculusDisplayPlugin::hmdPresent() { appDroppedFrames = frameStats.AppDroppedFrameCount; } _hardwareStats["app_dropped_frame_count"] = appDroppedFrames; + _hardwareStats["compositor_dropped_frame_count"] = compositorDroppedFrames; } _presentRate.increment(); } From e5901fa875de8701bf1d94abad5fd39d3e982df2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Dec 2016 11:09:09 -0800 Subject: [PATCH 107/118] Make getHardwareStats thread safe --- plugins/oculus/src/OculusDisplayPlugin.cpp | 16 +++++++++++++--- plugins/oculus/src/OculusDisplayPlugin.h | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index bad346b1ca..060823a748 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -22,6 +22,13 @@ const char* OculusDisplayPlugin::NAME { "Oculus Rift" }; static ovrPerfHudMode currentDebugMode = ovrPerfHud_Off; + +OculusDisplayPlugin::OculusDisplayPlugin() { + _appDroppedFrames.store(0); + _compositorDroppedFrames.store(0); +} + + bool OculusDisplayPlugin::internalActivate() { bool result = Parent::internalActivate(); currentDebugMode = ovrPerfHud_Off; @@ -158,15 +165,18 @@ void OculusDisplayPlugin::hmdPresent() { compositorDroppedFrames = frameStats.CompositorDroppedFrameCount; appDroppedFrames = frameStats.AppDroppedFrameCount; } - _hardwareStats["app_dropped_frame_count"] = appDroppedFrames; - _hardwareStats["compositor_dropped_frame_count"] = compositorDroppedFrames; + _appDroppedFrames.store(appDroppedFrames); + _compositorDroppedFrames.store(compositorDroppedFrames); } _presentRate.increment(); } QJsonObject OculusDisplayPlugin::getHardwareStats() const { - return _hardwareStats; + QJsonObject hardwareStats; + hardwareStats["app_dropped_frame_count"] = _appDroppedFrames.load(); + hardwareStats["compositor_dropped_frame_count"] = _compositorDroppedFrames.load(); + return hardwareStats; } bool OculusDisplayPlugin::isHmdMounted() const { diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index a953e01e2f..e44596d6e9 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -12,6 +12,7 @@ class OculusDisplayPlugin : public OculusBaseDisplayPlugin { using Parent = OculusBaseDisplayPlugin; public: + OculusDisplayPlugin(); ~OculusDisplayPlugin(); const QString getName() const override { return NAME; } @@ -36,6 +37,7 @@ private: gpu::FramebufferPointer _outputFramebuffer; bool _customized { false }; - QJsonObject _hardwareStats; + std::atomic_int _compositorDroppedFrames; + std::atomic_int _appDroppedFrames; }; From d00ae05e26a5b689db828117c38e90cbdef4862b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 28 Dec 2016 17:45:05 -0500 Subject: [PATCH 108/118] add background to forward renderer --- .../render-utils/src/RenderForwardTask.cpp | 28 +++++++++++++++++++ .../render-utils/src/RenderForwardTask.h | 10 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 4e971c6677..b8560ef2e8 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -80,6 +80,9 @@ RenderForwardTask::RenderForwardTask(CullFunctor cullFunctor) { const auto framebuffer = addJob("PrepareFramebuffer"); + addJob("DrawBackground", background); + + // bounds do not draw on stencil buffer, so they must come last addJob("DrawBounds", opaques); // Blit! @@ -173,6 +176,8 @@ void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContex RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + // Setup projection glm::mat4 projMat; Transform viewMat; @@ -197,3 +202,26 @@ void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContex } }); } + +void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& items) { + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + batch.enableSkybox(true); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + // Setup projection + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + renderItems(sceneContext, renderContext, items); + }); + args->_batch = nullptr; +} diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index a4839e18ec..90d2ceb79c 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -53,4 +53,12 @@ private: int _scaleLocation { -1 }; }; -#endif // hifi_RenderForwardTask_h \ No newline at end of file +class DrawBackground { +public: + using Inputs = render::ItemBounds; + using JobModel = render::Job::ModelI; + + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& background); +}; + +#endif // hifi_RenderForwardTask_h From c75dd236f2fad0334c71622bbe48086ecb5e0f00 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 29 Dec 2016 19:11:06 -0500 Subject: [PATCH 109/118] rm unused forward pipelines --- libraries/render-utils/src/RenderForwardTask.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index b8560ef2e8..ed4f3d27e3 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -31,14 +31,8 @@ #include using namespace render; -extern void initOverlay3DPipelines(render::ShapePlumber& plumber); -extern void initDeferredPipelines(render::ShapePlumber& plumber); RenderForwardTask::RenderForwardTask(CullFunctor cullFunctor) { - // Prepare the ShapePipelines - ShapePlumberPointer shapePlumber = std::make_shared(); - initDeferredPipelines(*shapePlumber); - // CPU jobs: // Fetch and cull the items from the scene const auto spatialSelection = addJob("FetchSceneSelection"); From 0c22fcb5c42896985e81de4816cfd7f4528e293f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 16:41:14 -0800 Subject: [PATCH 110/118] better handling of timers --- interface/src/ui/DiskCacheEditor.cpp | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 89 +++++++++++++++----- libraries/script-engine/src/ScriptEngine.h | 3 + 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp index c7f4bfe39b..1a7be8642b 100644 --- a/interface/src/ui/DiskCacheEditor.cpp +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -91,7 +91,7 @@ void DiskCacheEditor::makeDialog() { static const int REFRESH_INTERVAL = 100; // msec _refreshTimer = new QTimer(_dialog); - _refreshTimer->setInterval(REFRESH_INTERVAL); // 100ms, Qt::CoarseTimer acceptable + _refreshTimer->setInterval(REFRESH_INTERVAL); // Qt::CoarseTimer acceptable, no need for real time accuracy _refreshTimer->setSingleShot(false); QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh); _refreshTimer->start(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 45d37aa52e..fed1c6ffa4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -800,7 +800,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& _registeredHandlers[entityID] = RegisteredEventHandlers(); } CallbackList& handlersForEvent = _registeredHandlers[entityID][eventName]; - CallbackData handlerData = {handler, currentEntityIdentifier, currentSandboxURL}; + CallbackData handlerData = { handler, currentEntityIdentifier, currentSandboxURL }; handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler(). } @@ -840,6 +840,15 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi } void ScriptEngine::run() { + +#ifdef _WIN32 + // VS13 does not sleep_until unless it uses the system_clock, see: + // https://www.reddit.com/r/cpp_questions/comments/3o71ic/sleep_until_not_working_with_a_time_pointsteady/ + using clock = std::chrono::system_clock; +#else + using clock = std::chrono::high_resolution_clock; +#endif + if (DependencyManager::get()->isStopped()) { return; // bail early - avoid setting state in init(), as evaluate() will bail too } @@ -853,14 +862,6 @@ void ScriptEngine::run() { QScriptValue result = evaluate(_scriptContents, _fileNameString); -#ifdef _WIN32 - // VS13 does not sleep_until unless it uses the system_clock, see: - // https://www.reddit.com/r/cpp_questions/comments/3o71ic/sleep_until_not_working_with_a_time_pointsteady/ - using clock = std::chrono::system_clock; -#else - using clock = std::chrono::high_resolution_clock; -#endif - clock::time_point startTime = clock::now(); int thisFrame = 0; @@ -878,23 +879,48 @@ void ScriptEngine::run() { // Throttle to SCRIPT_FPS // We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will // calculate a sleepUntil to be the time from our start time until the original target - // sleepUntil for this frame. - const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); - clock::time_point targetSleepUntil(startTime + thisFrame++ * FRAME_DURATION); + // sleepUntil for this frame. This approach will allow us to "catch up" in the event + // that some of our script udpates/frames take a little bit longer than the target average + // to execute. + // NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach + const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); + clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION)); - // However, if our sleepUntil is not at least our average update time into the future - // it means our script is taking too long in it's updates, and we want to punish the - // script a little bit. So we will force the sleepUntil to be at least our averageUpdate - // time into the future. + // However, if our sleepUntil is not at least our average update and timer execution time + // into the future it means our script is taking too long in its updates, and we want to + // punish the script a little bit. So we will force the sleepUntil to be at least our + // averageUpdate + averageTimerPerFrame time into the future. auto averageUpdate = totalUpdates / thisFrame; - auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageUpdate); + auto averageTimerPerFrame = _totalTimerExecution / thisFrame; + auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame; + auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate); // We don't want to actually sleep for too long, because it causes our scripts to hang // on shutdown and stop... so we want to loop and sleep until we've spent our time in // purgatory, constantly checking to see if our script was asked to end + bool processedEvents = false; while (!_isFinished && clock::now() < sleepUntil) { + QCoreApplication::processEvents(); // before we sleep again, give events a chance to process - auto thisSleepUntil = std::min(sleepUntil, clock::now() + FRAME_DURATION); + processedEvents = true; + + // If after processing events, we're past due, exit asap + if (clock::now() >= sleepUntil) { + break; + } + + // determine how long before the next timer should fire, we'd ideally like to sleep just + // that long, so the next processEvents() will allow the timers to fire on time. + const std::chrono::microseconds minTimerTimeRemaining(USECS_PER_MSEC * getTimersRemainingTime()); + + // However, if we haven't yet slept at least as long as our average timer per frame, then we will + // punish the timers to at least wait as long as the average run time of the timers. + auto untilTimer = std::max(minTimerTimeRemaining, averageTimerPerFrame); + + // choose the closest time point, our + auto remainingSleepUntil = std::chrono::duration_cast(sleepUntil - clock::now()); + auto closestUntil = std::min(remainingSleepUntil, untilTimer); + auto thisSleepUntil = std::min(sleepUntil, clock::now() + closestUntil); std::this_thread::sleep_until(thisSleepUntil); } @@ -919,7 +945,10 @@ void ScriptEngine::run() { break; } - QCoreApplication::processEvents(); + // Only call this if we didn't processEvents as part of waiting for next frame + if (!processedEvents) { + QCoreApplication::processEvents(); + } if (_isFinished) { break; @@ -982,6 +1011,21 @@ void ScriptEngine::run() { emit doneRunning(); } +quint64 ScriptEngine::getTimersRemainingTime() { + quint64 minimumTime = USECS_PER_SECOND; // anything larger than this can be ignored + QMutableHashIterator i(_timerFunctionMap); + while (i.hasNext()) { + i.next(); + QTimer* timer = i.key(); + int remainingTime = timer->remainingTime(); + if (remainingTime >= 0) { + minimumTime = std::min((quint64)remainingTime, minimumTime); + } + } + return minimumTime; +} + + // NOTE: This is private because it must be called on the same thread that created the timers, which is why // we want to only call it in our own run "shutdown" processing. void ScriptEngine::stopAllTimers() { @@ -1077,7 +1121,12 @@ void ScriptEngine::timerFired() { // call the associated JS function, if it exists if (timerData.function.isValid()) { + auto preTimer = p_high_resolution_clock::now(); callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList()); + auto postTimer = p_high_resolution_clock::now(); + auto elapsed = (postTimer - preTimer); + _totalTimerExecution += std::chrono::duration_cast(elapsed); + } } @@ -1098,7 +1147,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int // make sure the timer stops when the script does connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop); - CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL}; + CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL }; _timerFunctionMap.insert(newTimer, timerData); newTimer->start(intervalMS); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1606e70d28..803aa7fa22 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -218,6 +218,7 @@ protected: void init(); bool evaluatePending() const { return _evaluatesPending > 0; } + quint64 getTimersRemainingTime(); void timerFired(); void stopAllTimers(); void stopAllTimersForEntityScript(const EntityItemID& entityID); @@ -252,6 +253,8 @@ protected: std::function _emitScriptUpdates{ [](){ return true; } }; std::recursive_mutex _lock; + + std::chrono::microseconds _totalTimerExecution { 0 }; }; #endif // hifi_ScriptEngine_h From 4b9345d7c6eda6358dc98923a3420280cdf7f88e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 16:45:16 -0800 Subject: [PATCH 111/118] cleanup accidental change --- libraries/script-engine/src/ScriptEngine.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fed1c6ffa4..03b01e1b9c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -840,15 +840,6 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi } void ScriptEngine::run() { - -#ifdef _WIN32 - // VS13 does not sleep_until unless it uses the system_clock, see: - // https://www.reddit.com/r/cpp_questions/comments/3o71ic/sleep_until_not_working_with_a_time_pointsteady/ - using clock = std::chrono::system_clock; -#else - using clock = std::chrono::high_resolution_clock; -#endif - if (DependencyManager::get()->isStopped()) { return; // bail early - avoid setting state in init(), as evaluate() will bail too } @@ -862,6 +853,14 @@ void ScriptEngine::run() { QScriptValue result = evaluate(_scriptContents, _fileNameString); +#ifdef _WIN32 + // VS13 does not sleep_until unless it uses the system_clock, see: + // https://www.reddit.com/r/cpp_questions/comments/3o71ic/sleep_until_not_working_with_a_time_pointsteady/ + using clock = std::chrono::system_clock; +#else + using clock = std::chrono::high_resolution_clock; +#endif + clock::time_point startTime = clock::now(); int thisFrame = 0; From 6c86675f24fa5c46badbafe6e9aa854b1bc9e4c6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 18:36:32 -0800 Subject: [PATCH 112/118] tweaks to bow script to debug judder --- unpublishedScripts/marketplace/bow/bow.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 818960e335..61c52a6103 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -154,6 +154,7 @@ }, continueEquip: function(entityID, args) { this.deltaTime = checkInterval(); + print("continueEquip deltaTime:" + this.deltaTime); //debounce during debugging -- maybe we're updating too fast? if (USE_DEBOUNCE === true) { this.sinceLastUpdate = this.sinceLastUpdate + this.deltaTime; @@ -374,8 +375,8 @@ this.pullBackDistance = 0; this.state = STATE_ARROW_GRABBED; } else { - this.updateString(); this.updateArrowPositionInNotch(false, false); + this.updateString(); } } if (this.state === STATE_ARROW_GRABBED) { From 73cb100d7690bc915760f2e73a72ea1984077cb3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 19:00:06 -0800 Subject: [PATCH 113/118] more debugging of bow --- unpublishedScripts/marketplace/bow/bow.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 61c52a6103..183703ba91 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -593,3 +593,8 @@ return bow; }); + +var TIMER_INTERVAL = 5; +var RPCkicker = Script.setInterval(function() { + // do nothing, but make sure we get RPC messages in a reliable way +},TIMER_INTERVAL); From 1bf39652235f84b28fcb008671fcb54e9e59934b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 19:03:29 -0800 Subject: [PATCH 114/118] more debugging of bow --- unpublishedScripts/marketplace/bow/bow.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 183703ba91..6850ca48c6 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -595,6 +595,11 @@ }); var TIMER_INTERVAL = 5; +var lastRPC = Date.now(); var RPCkicker = Script.setInterval(function() { // do nothing, but make sure we get RPC messages in a reliable way + var now = Date.now(); + deltaTime = now - lastRPC; + lastRPC = now; + print("RPCkicker interval:"+deltaTime); },TIMER_INTERVAL); From 65c8d4b16b5513f513c9a61da6b1fa22829e9e48 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 19:16:55 -0800 Subject: [PATCH 115/118] more debugging of bow --- unpublishedScripts/marketplace/bow/bow.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 6850ca48c6..d42f1c223d 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -591,15 +591,16 @@ Messages.subscribe('Hifi-Object-Manipulation'); Messages.messageReceived.connect(bow.handleMessages); - return bow; -}); + var TIMER_INTERVAL = 5; + var lastRPC = Date.now(); + var RPCkicker = Script.setInterval(function() { + // do nothing, but make sure we get RPC messages in a reliable way + var now = Date.now(); + deltaTime = now - lastRPC; + lastRPC = now; + print("RPCkicker interval:"+deltaTime); + },TIMER_INTERVAL); -var TIMER_INTERVAL = 5; -var lastRPC = Date.now(); -var RPCkicker = Script.setInterval(function() { - // do nothing, but make sure we get RPC messages in a reliable way - var now = Date.now(); - deltaTime = now - lastRPC; - lastRPC = now; - print("RPCkicker interval:"+deltaTime); -},TIMER_INTERVAL); + + return bow; +}); \ No newline at end of file From b1d556aee2efd01a2b4815d49dfd079d68e0e6d8 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Dec 2016 19:21:18 -0800 Subject: [PATCH 116/118] more debugging of bow --- unpublishedScripts/marketplace/bow/bow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index d42f1c223d..5afeb99ce4 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -598,7 +598,7 @@ var now = Date.now(); deltaTime = now - lastRPC; lastRPC = now; - print("RPCkicker interval:"+deltaTime); + //print("RPCkicker interval:"+deltaTime); },TIMER_INTERVAL); From 1fcfc9444b200d0707fceed4152c31af1473077e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Dec 2016 08:12:44 -0800 Subject: [PATCH 117/118] revert bow.js changes --- unpublishedScripts/marketplace/bow/bow.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 5afeb99ce4..818960e335 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -154,7 +154,6 @@ }, continueEquip: function(entityID, args) { this.deltaTime = checkInterval(); - print("continueEquip deltaTime:" + this.deltaTime); //debounce during debugging -- maybe we're updating too fast? if (USE_DEBOUNCE === true) { this.sinceLastUpdate = this.sinceLastUpdate + this.deltaTime; @@ -375,8 +374,8 @@ this.pullBackDistance = 0; this.state = STATE_ARROW_GRABBED; } else { - this.updateArrowPositionInNotch(false, false); this.updateString(); + this.updateArrowPositionInNotch(false, false); } } if (this.state === STATE_ARROW_GRABBED) { @@ -591,16 +590,5 @@ Messages.subscribe('Hifi-Object-Manipulation'); Messages.messageReceived.connect(bow.handleMessages); - var TIMER_INTERVAL = 5; - var lastRPC = Date.now(); - var RPCkicker = Script.setInterval(function() { - // do nothing, but make sure we get RPC messages in a reliable way - var now = Date.now(); - deltaTime = now - lastRPC; - lastRPC = now; - //print("RPCkicker interval:"+deltaTime); - },TIMER_INTERVAL); - - return bow; -}); \ No newline at end of file +}); From a42ac89a6d83c909a45b75b31361c65243563836 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 30 Dec 2016 13:47:31 -0800 Subject: [PATCH 118/118] Faster HRTF New AVX2 implementation of FIR_1x4() achieves 24 FLOPS per clock on HSW/BDW/SKL Removed old AVX implementation 32-byte alignment of SIMD buffers and tables Fixed the poorly optimized code produced by GCC -O2 --- libraries/audio/src/AudioHRTF.cpp | 62 +++++++------ libraries/audio/src/AudioHRTFData.h | 58 +++++++------ libraries/audio/src/avx/AudioHRTF_avx.cpp | 96 --------------------- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 94 ++++++++++++++++++++ 4 files changed, 164 insertions(+), 146 deletions(-) delete mode 100644 libraries/audio/src/avx/AudioHRTF_avx.cpp create mode 100644 libraries/audio/src/avx2/AudioHRTF_avx2.cpp diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 5984187203..84e3622498 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -16,6 +16,14 @@ #include "AudioHRTF.h" #include "AudioHRTFData.h" +#if defined(_MSC_VER) +#define ALIGN32 __declspec(align(32)) +#elif defined(__GNUC__) +#define ALIGN32 __attribute__((aligned(32))) +#else +#define ALIGN32 +#endif + #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif @@ -30,7 +38,7 @@ // Transients in the time-varying Thiran allpass filter are eliminated by the initial delay. // Valimaki, Laakso. "Elimination of Transients in Time-Varying Allpass Fractional Delay Filters" // -static const float crossfadeTable[HRTF_BLOCK] = { +ALIGN32 static const float crossfadeTable[HRTF_BLOCK] = { 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 0.9999545513f, 0.9998182135f, 0.9995910114f, 0.9992729863f, 0.9988641959f, 0.9983647147f, 0.9977746334f, 0.9970940592f, 0.9963231160f, 0.9954619438f, 0.9945106993f, 0.9934695553f, 0.9923387012f, 0.9911183425f, 0.9898087010f, 0.9884100149f, @@ -192,25 +200,29 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float for (int k = 0; k < HRTF_TAPS; k += 4) { - acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-0]), _mm_loadu_ps(&ps[k+0]))); - acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-0]), _mm_loadu_ps(&ps[k+0]))); - acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-0]), _mm_loadu_ps(&ps[k+0]))); - acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-0]), _mm_loadu_ps(&ps[k+0]))); + __m128 x0 = _mm_loadu_ps(&ps[k+0]); + acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-0]), x0)); + acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-0]), x0)); + acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-0]), x0)); + acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-0]), x0)); - acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-1]), _mm_loadu_ps(&ps[k+1]))); - acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-1]), _mm_loadu_ps(&ps[k+1]))); - acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-1]), _mm_loadu_ps(&ps[k+1]))); - acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-1]), _mm_loadu_ps(&ps[k+1]))); + __m128 x1 = _mm_loadu_ps(&ps[k+1]); + acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-1]), x1)); + acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-1]), x1)); + acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-1]), x1)); + acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-1]), x1)); - acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-2]), _mm_loadu_ps(&ps[k+2]))); - acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-2]), _mm_loadu_ps(&ps[k+2]))); - acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-2]), _mm_loadu_ps(&ps[k+2]))); - acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-2]), _mm_loadu_ps(&ps[k+2]))); + __m128 x2 = _mm_loadu_ps(&ps[k+2]); + acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-2]), x2)); + acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-2]), x2)); + acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-2]), x2)); + acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-2]), x2)); - acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-3]), _mm_loadu_ps(&ps[k+3]))); - acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-3]), _mm_loadu_ps(&ps[k+3]))); - acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-3]), _mm_loadu_ps(&ps[k+3]))); - acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-3]), _mm_loadu_ps(&ps[k+3]))); + __m128 x3 = _mm_loadu_ps(&ps[k+3]); + acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_load1_ps(&coef0[-k-3]), x3)); + acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_load1_ps(&coef1[-k-3]), x3)); + acc2 = _mm_add_ps(acc2, _mm_mul_ps(_mm_load1_ps(&coef2[-k-3]), x3)); + acc3 = _mm_add_ps(acc3, _mm_mul_ps(_mm_load1_ps(&coef3[-k-3]), x3)); } _mm_storeu_ps(&dst0[i], acc0); @@ -226,11 +238,11 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float #include "CPUDetect.h" -void FIR_1x4_AVX(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); +void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { - static auto f = cpuSupportsAVX() ? FIR_1x4_AVX : FIR_1x4_SSE; + static auto f = cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE; (*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch } @@ -842,12 +854,12 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, assert(index < HRTF_TABLES); assert(numFrames == HRTF_BLOCK); - float in[HRTF_TAPS + HRTF_BLOCK]; // mono - float firCoef[4][HRTF_TAPS]; // 4-channel - float firBuffer[4][HRTF_DELAY + HRTF_BLOCK]; // 4-channel - float bqCoef[5][8]; // 4-channel (interleaved) - float bqBuffer[4 * HRTF_BLOCK]; // 4-channel (interleaved) - int delay[4]; // 4-channel (interleaved) + ALIGN32 float in[HRTF_TAPS + HRTF_BLOCK]; // mono + ALIGN32 float firCoef[4][HRTF_TAPS]; // 4-channel + ALIGN32 float firBuffer[4][HRTF_DELAY + HRTF_BLOCK]; // 4-channel + ALIGN32 float bqCoef[5][8]; // 4-channel (interleaved) + ALIGN32 float bqBuffer[4 * HRTF_BLOCK]; // 4-channel (interleaved) + int delay[4]; // 4-channel (interleaved) // to avoid polluting the cache, old filters are recomputed instead of stored setFilters(firCoef, bqCoef, delay, index, _azimuthState, _distanceState, _gainState, L0); diff --git a/libraries/audio/src/AudioHRTFData.h b/libraries/audio/src/AudioHRTFData.h index e317dee7c7..b2d3ebf847 100644 --- a/libraries/audio/src/AudioHRTFData.h +++ b/libraries/audio/src/AudioHRTFData.h @@ -30,6 +30,14 @@ // 6) Truncate filter length to 2.5ms using rectangular window with 8-tap Hanning taper // +#if defined(_MSC_VER) +#define ALIGN32 __declspec(align(32)) +#elif defined(__GNUC__) +#define ALIGN32 __attribute__((aligned(32))) +#else +#define ALIGN32 +#endif + static const float itd_1002_table[HRTF_AZIMUTHS] = { -0.07851f, 0.85414f, 1.77170f, 2.71137f, 3.71065f, 4.74907f, 5.79892f, 6.82396f, 7.82837f, 8.80796f, 9.75426f, 10.68332f, 11.59979f, 12.48520f, 13.36135f, 14.19234f, @@ -42,7 +50,7 @@ static const float itd_1002_table[HRTF_AZIMUTHS] = { -8.39670f, -7.23606f, -6.09663f, -5.05593f, -4.06186f, -3.07465f, -2.06122f, -1.05417f, }; -static const float ir_1002_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1002_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.341559e-01f, 1.886116e-02f, 2.677664e-01f, -7.037183e-02f, -4.147236e-02f, -2.761588e-01f, 2.310035e-01f, -1.643133e-01f, @@ -1497,7 +1505,7 @@ static const float itd_1003_table[HRTF_AZIMUTHS] = { -6.64380f, -5.73462f, -4.83364f, -3.97025f, -3.08925f, -2.16621f, -1.19364f, -0.20709f, }; -static const float ir_1003_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1003_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.266240e-01f, 1.260510e-01f, 5.051008e-02f, -3.536678e-01f, 2.462246e-02f, 4.465557e-02f, 6.813228e-02f, -6.063477e-02f, @@ -2952,7 +2960,7 @@ static const float itd_1004_table[HRTF_AZIMUTHS] = { -7.55720f, -6.55578f, -5.59246f, -4.69657f, -3.80733f, -2.88567f, -1.90337f, -0.89923f, }; -static const float ir_1004_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1004_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.326633e-01f, 4.279429e-01f, -5.910516e-02f, -2.480760e-01f, -9.903029e-02f, 9.215562e-02f, -2.893536e-02f, 5.464364e-02f, @@ -4407,7 +4415,7 @@ static const float itd_1005_table[HRTF_AZIMUTHS] = { -6.80079f, -6.03878f, -5.25100f, -4.34973f, -3.39268f, -2.41226f, -1.45444f, -0.50375f, }; -static const float ir_1005_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1005_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.515557e-01f, 1.208618e-01f, 3.238278e-01f, -3.605847e-01f, -3.354420e-02f, -1.829174e-01f, 2.309960e-01f, -1.744711e-01f, @@ -5862,7 +5870,7 @@ static const float itd_1007_table[HRTF_AZIMUTHS] = { -7.68135f, -6.69801f, -5.72186f, -4.72708f, -3.74413f, -2.77373f, -1.79032f, -0.81823f, }; -static const float ir_1007_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1007_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 6.544936e-01f, 2.820574e-01f, 1.850652e-01f, -2.597811e-01f, -5.585250e-02f, -7.975905e-02f, 8.143960e-02f, -5.044548e-02f, @@ -7317,7 +7325,7 @@ static const float itd_1012_table[HRTF_AZIMUTHS] = { -7.32159f, -6.30684f, -5.31969f, -4.40260f, -3.50567f, -2.60925f, -1.70893f, -0.80401f, }; -static const float ir_1012_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1012_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.505165e-01f, 9.074762e-02f, 3.296598e-01f, -5.213905e-01f, 1.348379e-01f, -1.828924e-01f, 1.400077e-01f, -4.071996e-02f, @@ -8772,7 +8780,7 @@ static const float itd_1014_table[HRTF_AZIMUTHS] = { -7.51312f, -6.52705f, -5.56262f, -4.72113f, -3.90664f, -3.07768f, -2.22719f, -1.37514f, }; -static const float ir_1014_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1014_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 6.542071e-01f, 4.575563e-01f, 1.118072e-02f, -1.823464e-01f, -2.222339e-01f, 1.371357e-01f, 7.027919e-03f, -5.534852e-02f, @@ -10227,7 +10235,7 @@ static const float itd_1017_table[HRTF_AZIMUTHS] = { -7.46925f, -6.49073f, -5.52501f, -4.62178f, -3.74041f, -2.86207f, -1.97362f, -1.07512f, }; -static const float ir_1017_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1017_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.470867e-01f, 2.686078e-01f, 2.097923e-01f, -2.935018e-01f, -8.687224e-02f, -4.547367e-02f, 6.920631e-03f, 3.752071e-02f, @@ -11682,7 +11690,7 @@ static const float itd_1020_table[HRTF_AZIMUTHS] = { -8.28071f, -7.36311f, -6.43732f, -5.49298f, -4.53728f, -3.57601f, -2.59830f, -1.63297f, }; -static const float ir_1020_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1020_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 6.953847e-01f, 3.081256e-01f, 2.474324e-01f, -3.025226e-01f, -1.119181e-01f, -4.966299e-02f, 5.727889e-02f, 6.715016e-03f, @@ -13137,7 +13145,7 @@ static const float itd_1021_table[HRTF_AZIMUTHS] = { -8.12772f, -7.17689f, -6.23068f, -5.27554f, -4.32391f, -3.38489f, -2.46445f, -1.54407f, }; -static const float ir_1021_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1021_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.807186e-01f, 3.835520e-01f, 1.208801e-01f, -4.044311e-01f, -5.188029e-02f, -7.750225e-02f, 1.739668e-01f, -6.599168e-02f, @@ -14592,7 +14600,7 @@ static const float itd_1022_table[HRTF_AZIMUTHS] = { -7.19675f, -6.30334f, -5.39609f, -4.47018f, -3.53964f, -2.62393f, -1.75389f, -0.90222f, }; -static const float ir_1022_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1022_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.053226e-01f, 2.645844e-01f, 2.462055e-01f, -2.145682e-01f, -1.333283e-01f, -1.751403e-01f, 2.721890e-01f, -1.743790e-01f, @@ -16047,7 +16055,7 @@ static const float itd_1026_table[HRTF_AZIMUTHS] = { -7.45209f, -6.46598f, -5.49746f, -4.54220f, -3.60610f, -2.68084f, -1.74087f, -0.80841f, }; -static const float ir_1026_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1026_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.150396e-01f, 3.144234e-01f, 9.132840e-02f, -2.128668e-01f, -1.899010e-01f, 1.362356e-01f, -4.105226e-02f, 4.896281e-02f, @@ -17502,7 +17510,7 @@ static const float itd_1028_table[HRTF_AZIMUTHS] = { -7.80099f, -6.89255f, -5.95721f, -5.04107f, -4.11968f, -3.20233f, -2.33316f, -1.46289f, }; -static const float ir_1028_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1028_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.491360e-01f, 2.952796e-01f, -1.585342e-01f, -3.497386e-01f, 1.204260e-01f, -4.886012e-02f, 5.238760e-02f, -8.209077e-03f, @@ -18957,7 +18965,7 @@ static const float itd_1038_table[HRTF_AZIMUTHS] = { -6.69661f, -5.65906f, -4.62851f, -3.63493f, -2.66802f, -1.71997f, -0.76853f, 0.18497f, }; -static const float ir_1038_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1038_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.325991e-01f, 1.817283e-01f, 5.397613e-02f, -4.121773e-01f, -7.921759e-03f, -4.009945e-02f, 1.499187e-01f, -1.838252e-02f, @@ -20412,7 +20420,7 @@ static const float itd_1041_table[HRTF_AZIMUTHS] = { -7.03257f, -6.07458f, -5.13664f, -4.24453f, -3.37177f, -2.49083f, -1.55807f, -0.62014f, }; -static const float ir_1041_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1041_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.012368e-01f, 2.006662e-01f, 3.173636e-01f, -2.865733e-01f, 1.345042e-01f, -5.030394e-01f, 3.717757e-01f, -1.138039e-01f, @@ -21867,7 +21875,7 @@ static const float itd_1042_table[HRTF_AZIMUTHS] = { -7.79822f, -6.84403f, -5.88862f, -4.94525f, -3.99704f, -3.03547f, -2.06207f, -1.07916f, }; -static const float ir_1042_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1042_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.114429e-01f, 2.201994e-03f, 3.703525e-01f, -4.825957e-01f, 1.210277e-01f, -2.471091e-01f, 1.766662e-01f, -5.840113e-03f, @@ -23322,7 +23330,7 @@ static const float itd_1043_table[HRTF_AZIMUTHS] = { -6.81973f, -5.86664f, -4.92096f, -3.99232f, -3.07973f, -2.16321f, -1.20142f, -0.22538f, }; -static const float ir_1043_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1043_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.339447e-01f, 1.339343e-01f, 4.031645e-01f, -4.891909e-01f, 8.751389e-02f, -2.110783e-01f, 2.573841e-01f, -1.050324e-01f, @@ -24777,7 +24785,7 @@ static const float itd_1044_table[HRTF_AZIMUTHS] = { -7.31965f, -6.37963f, -5.45379f, -4.54748f, -3.59370f, -2.59525f, -1.67705f, -0.73882f, }; -static const float ir_1044_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1044_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.028871e-01f, 2.381998e-01f, 4.686725e-01f, -5.412304e-01f, 1.262568e-01f, -3.198619e-01f, 1.963468e-01f, -4.016186e-02f, @@ -26232,7 +26240,7 @@ static const float itd_1047_table[HRTF_AZIMUTHS] = { -9.01225f, -7.93667f, -6.85884f, -5.78919f, -4.72064f, -3.66640f, -2.66295f, -1.65780f, }; -static const float ir_1047_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1047_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 7.788578e-01f, 1.598904e-01f, 2.366520e-01f, -3.524184e-01f, -8.784474e-03f, -5.144472e-02f, 8.679429e-02f, -1.634258e-02f, @@ -27687,7 +27695,7 @@ static const float itd_1048_table[HRTF_AZIMUTHS] = { -7.15985f, -6.30472f, -5.41513f, -4.54994f, -3.62385f, -2.66142f, -1.79111f, -0.94033f, }; -static const float ir_1048_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1048_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.865287e-01f, 2.972076e-01f, -1.305391e-01f, -1.213860e-01f, -1.948535e-01f, 1.458427e-01f, -8.912857e-02f, 9.493978e-02f, @@ -29142,7 +29150,7 @@ static const float itd_1050_table[HRTF_AZIMUTHS] = { -6.52690f, -5.58085f, -4.64474f, -3.71658f, -2.80444f, -1.92096f, -1.07543f, -0.23450f, }; -static const float ir_1050_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1050_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.005889e-01f, -6.452200e-02f, 3.675525e-01f, -4.309962e-01f, 7.086621e-02f, -9.161573e-02f, -4.290351e-02f, 9.057393e-02f, @@ -30597,7 +30605,7 @@ static const float itd_1052_table[HRTF_AZIMUTHS] = { -6.50194f, -5.61262f, -4.72534f, -3.84869f, -2.97504f, -2.10269f, -1.23783f, -0.36766f, }; -static const float ir_1052_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1052_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 6.650009e-01f, 3.507944e-01f, -3.274164e-02f, -1.830690e-01f, -7.720853e-02f, 1.030789e-01f, 3.877069e-02f, -5.674440e-02f, @@ -32052,7 +32060,7 @@ static const float itd_1054_table[HRTF_AZIMUTHS] = { -7.35642f, -6.36606f, -5.37262f, -4.40394f, -3.44967f, -2.51333f, -1.59834f, -0.68300f, }; -static const float ir_1054_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1054_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.629450e-01f, 1.677356e-01f, 1.467365e-01f, -3.248726e-01f, -5.105235e-02f, -5.031096e-02f, 1.796471e-01f, -1.298094e-01f, @@ -33507,7 +33515,7 @@ static const float itd_1056_table[HRTF_AZIMUTHS] = { -6.99437f, -5.82430f, -4.73408f, -3.76713f, -2.88870f, -2.05251f, -1.18172f, -0.32736f, }; -static const float ir_1056_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1056_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 8.031418e-01f, 2.411323e-01f, 1.417951e-01f, -2.476192e-01f, -1.076012e-01f, 1.009190e-01f, 7.761394e-02f, -1.250722e-01f, @@ -34962,7 +34970,7 @@ static const float itd_1058_table[HRTF_AZIMUTHS] = { -7.78555f, -6.81447f, -5.85685f, -4.89466f, -3.93902f, -2.98660f, -2.01925f, -1.05758f, }; -static const float ir_1058_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { +ALIGN32 static const float ir_1058_table[HRTF_AZIMUTHS][2][HRTF_TAPS] = { // azimuth = 0 {{ 9.307292e-01f, 5.592706e-02f, 2.567367e-01f, -4.525413e-01f, 1.378666e-01f, -2.503950e-01f, 1.983286e-01f, 5.925522e-03f, diff --git a/libraries/audio/src/avx/AudioHRTF_avx.cpp b/libraries/audio/src/avx/AudioHRTF_avx.cpp deleted file mode 100644 index b103bf015c..0000000000 --- a/libraries/audio/src/avx/AudioHRTF_avx.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// -// AudioHRTF_avx.cpp -// libraries/audio/src/avx -// -// Created by Ken Cooke on 1/17/16. -// 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 -// - -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) - -#include -#include - -#include "../AudioHRTF.h" - -#ifndef __AVX__ -#error Must be compiled with /arch:AVX or -mavx. -#endif - -// 1 channel input, 4 channel output -void FIR_1x4_AVX(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { - - float* coef0 = coef[0] + HRTF_TAPS - 1; // process backwards - float* coef1 = coef[1] + HRTF_TAPS - 1; - float* coef2 = coef[2] + HRTF_TAPS - 1; - float* coef3 = coef[3] + HRTF_TAPS - 1; - - assert(numFrames % 8 == 0); - - for (int i = 0; i < numFrames; i += 8) { - - __m256 acc0 = _mm256_setzero_ps(); - __m256 acc1 = _mm256_setzero_ps(); - __m256 acc2 = _mm256_setzero_ps(); - __m256 acc3 = _mm256_setzero_ps(); - - float* ps = &src[i - HRTF_TAPS + 1]; // process forwards - - assert(HRTF_TAPS % 8 == 0); - - for (int k = 0; k < HRTF_TAPS; k += 8) { - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-0]), _mm256_loadu_ps(&ps[k+0]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-0]), _mm256_loadu_ps(&ps[k+0]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-0]), _mm256_loadu_ps(&ps[k+0]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-0]), _mm256_loadu_ps(&ps[k+0]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-1]), _mm256_loadu_ps(&ps[k+1]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-1]), _mm256_loadu_ps(&ps[k+1]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-1]), _mm256_loadu_ps(&ps[k+1]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-1]), _mm256_loadu_ps(&ps[k+1]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-2]), _mm256_loadu_ps(&ps[k+2]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-2]), _mm256_loadu_ps(&ps[k+2]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-2]), _mm256_loadu_ps(&ps[k+2]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-2]), _mm256_loadu_ps(&ps[k+2]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-3]), _mm256_loadu_ps(&ps[k+3]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-3]), _mm256_loadu_ps(&ps[k+3]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-3]), _mm256_loadu_ps(&ps[k+3]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-3]), _mm256_loadu_ps(&ps[k+3]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-4]), _mm256_loadu_ps(&ps[k+4]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-4]), _mm256_loadu_ps(&ps[k+4]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-4]), _mm256_loadu_ps(&ps[k+4]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-4]), _mm256_loadu_ps(&ps[k+4]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-5]), _mm256_loadu_ps(&ps[k+5]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-5]), _mm256_loadu_ps(&ps[k+5]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-5]), _mm256_loadu_ps(&ps[k+5]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-5]), _mm256_loadu_ps(&ps[k+5]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-6]), _mm256_loadu_ps(&ps[k+6]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-6]), _mm256_loadu_ps(&ps[k+6]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-6]), _mm256_loadu_ps(&ps[k+6]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-6]), _mm256_loadu_ps(&ps[k+6]))); - - acc0 = _mm256_add_ps(acc0, _mm256_mul_ps(_mm256_broadcast_ss(&coef0[-k-7]), _mm256_loadu_ps(&ps[k+7]))); - acc1 = _mm256_add_ps(acc1, _mm256_mul_ps(_mm256_broadcast_ss(&coef1[-k-7]), _mm256_loadu_ps(&ps[k+7]))); - acc2 = _mm256_add_ps(acc2, _mm256_mul_ps(_mm256_broadcast_ss(&coef2[-k-7]), _mm256_loadu_ps(&ps[k+7]))); - acc3 = _mm256_add_ps(acc3, _mm256_mul_ps(_mm256_broadcast_ss(&coef3[-k-7]), _mm256_loadu_ps(&ps[k+7]))); - } - - _mm256_storeu_ps(&dst0[i], acc0); - _mm256_storeu_ps(&dst1[i], acc1); - _mm256_storeu_ps(&dst2[i], acc2); - _mm256_storeu_ps(&dst3[i], acc3); - } - - _mm256_zeroupper(); -} - -#endif diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp new file mode 100644 index 0000000000..452ceb7f4c --- /dev/null +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -0,0 +1,94 @@ +// +// AudioHRTF_avx2.cpp +// libraries/audio/src +// +// Created by Ken Cooke on 1/17/16. +// 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 +// + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) + +#include +#include // AVX2 + +#include "../AudioHRTF.h" + +#ifndef __AVX2__ +#error Must be compiled with /arch:AVX2 or -mavx2 -mfma. +#endif + +#if defined(__GNUC__) && !defined(__clang__) +// for some reason, GCC -O2 results in poorly optimized code +#pragma GCC optimize("Os") +#endif + +// 1 channel input, 4 channel output +void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { + + float* coef0 = coef[0] + HRTF_TAPS - 1; // process backwards + float* coef1 = coef[1] + HRTF_TAPS - 1; + float* coef2 = coef[2] + HRTF_TAPS - 1; + float* coef3 = coef[3] + HRTF_TAPS - 1; + + assert(numFrames % 8 == 0); + + for (int i = 0; i < numFrames; i += 8) { + + __m256 acc0 = _mm256_setzero_ps(); + __m256 acc1 = _mm256_setzero_ps(); + __m256 acc2 = _mm256_setzero_ps(); + __m256 acc3 = _mm256_setzero_ps(); + __m256 acc4 = _mm256_setzero_ps(); + __m256 acc5 = _mm256_setzero_ps(); + __m256 acc6 = _mm256_setzero_ps(); + __m256 acc7 = _mm256_setzero_ps(); + + float* ps = &src[i - HRTF_TAPS + 1]; // process forwards + + assert(HRTF_TAPS % 4 == 0); + + for (int k = 0; k < HRTF_TAPS; k += 4) { + + __m256 x0 = _mm256_loadu_ps(&ps[k+0]); + acc0 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef0[-k-0]), x0, acc0); + acc1 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef1[-k-0]), x0, acc1); + acc2 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef2[-k-0]), x0, acc2); + acc3 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef3[-k-0]), x0, acc3); + + __m256 x1 = _mm256_loadu_ps(&ps[k+1]); + acc4 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef0[-k-1]), x1, acc4); + acc5 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef1[-k-1]), x1, acc5); + acc6 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef2[-k-1]), x1, acc6); + acc7 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef3[-k-1]), x1, acc7); + + __m256 x2 = _mm256_loadu_ps(&ps[k+2]); + acc0 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef0[-k-2]), x2, acc0); + acc1 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef1[-k-2]), x2, acc1); + acc2 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef2[-k-2]), x2, acc2); + acc3 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef3[-k-2]), x2, acc3); + + __m256 x3 = _mm256_loadu_ps(&ps[k+3]); + acc4 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef0[-k-3]), x3, acc4); + acc5 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef1[-k-3]), x3, acc5); + acc6 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef2[-k-3]), x3, acc6); + acc7 = _mm256_fmadd_ps(_mm256_broadcast_ss(&coef3[-k-3]), x3, acc7); + } + + acc0 = _mm256_add_ps(acc0, acc4); + acc1 = _mm256_add_ps(acc1, acc5); + acc2 = _mm256_add_ps(acc2, acc6); + acc3 = _mm256_add_ps(acc3, acc7); + + _mm256_storeu_ps(&dst0[i], acc0); + _mm256_storeu_ps(&dst1[i], acc1); + _mm256_storeu_ps(&dst2[i], acc2); + _mm256_storeu_ps(&dst3[i], acc3); + } + + _mm256_zeroupper(); +} + +#endif