diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2e6e909312..8b277d4c20 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -116,6 +116,22 @@ Item { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } + StatText { + visible: root.expanded + text: "Total picks:\n " + + root.stylusPicksCount + " styluses\n " + + root.rayPicksCount + " rays\n " + + root.parabolaPicksCount + " parabolas\n " + + root.collisionPicksCount + " colliders" + } + StatText { + visible: root.expanded + text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " + + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " + + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " + + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " + + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w + } } } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ce1cd51de1..16e2bb955f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -146,6 +147,20 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate()); + auto pickManager = DependencyManager::get(); + if (pickManager && (_expanded || force)) { + std::vector totalPicks = pickManager->getTotalPickCounts(); + STAT_UPDATE(stylusPicksCount, totalPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksCount, totalPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksCount, totalPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksCount, totalPicks[PickQuery::Collision]); + std::vector updatedPicks = pickManager->getUpdatedPickCounts(); + STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksUpdated, updatedPicks[PickQuery::Collision]); + } + auto bandwidthRecorder = DependencyManager::get(); STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); @@ -285,7 +300,7 @@ void Stats::updateStats(bool force) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; - } // expanded avatar column + } // Fourth column, octree stats int serverCount = 0; diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index cf624b54c3..7845a540de 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -22,7 +22,6 @@ public: \ private: \ type _##name{ initialValue }; - /**jsdoc * @namespace Stats * @@ -168,6 +167,15 @@ private: \ * @property {number} implicitHeight * * @property {object} layer - Read-only. + + * @property {number} stylusPicksCount - Read-only. + * @property {number} rayPicksCount - Read-only. + * @property {number} parabolaPicksCount - Read-only. + * @property {number} collisionPicksCount - Read-only. + * @property {Vec4} stylusPicksUpdated - Read-only. + * @property {Vec4} rayPicksUpdated - Read-only. + * @property {Vec4} parabolaPicksUpdated - Read-only. + * @property {Vec4} collisionPicksUpdated - Read-only. */ // Properties from x onwards are QQuickItem properties. @@ -285,6 +293,15 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, avatarSimulationTime, 0) Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged) + STATS_PROPERTY(int, stylusPicksCount, 0) + STATS_PROPERTY(int, rayPicksCount, 0) + STATS_PROPERTY(int, parabolaPicksCount, 0) + STATS_PROPERTY(int, collisionPicksCount, 0) + STATS_PROPERTY(QVector4D, stylusPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, rayPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, parabolaPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, collisionPicksUpdated, QVector4D(0, 0, 0, 0)) + public: static Stats* getInstance(); @@ -1245,6 +1262,62 @@ signals: * @function Stats.update */ + /**jsdoc + * Triggered when the value of the stylusPicksCount property changes. + * @function Stats.stylusPicksCountChanged + * @returns {Signal} + */ + void stylusPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksCount property changes. + * @function Stats.rayPicksCountChanged + * @returns {Signal} + */ + void rayPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksCount property changes. + * @function Stats.parabolaPicksCountChanged + * @returns {Signal} + */ + void parabolaPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksCount property changes. + * @function Stats.collisionPicksCountChanged + * @returns {Signal} + */ + void collisionPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the stylusPicksUpdated property changes. + * @function Stats.stylusPicksUpdatedChanged + * @returns {Signal} + */ + void stylusPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksUpdated property changes. + * @function Stats.rayPicksUpdatedChanged + * @returns {Signal} + */ + void rayPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksUpdated property changes. + * @function Stats.parabolaPicksUpdatedChanged + * @returns {Signal} + */ + void parabolaPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksUpdated property changes. + * @function Stats.collisionPicksUpdatedChanged + * @returns {Signal} + */ + void collisionPicksUpdatedChanged(); + private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon{ true }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 63f27dd170..af8d4fac86 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -571,7 +571,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o } glm::mat4 wtvMatrix = worldToVoxelMatrix(); - glm::mat4 vtwMatrix = voxelToWorldMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index e8e11c0ee1..b7288755a6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -145,7 +145,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -153,8 +152,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = distance; EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, @@ -285,7 +282,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -293,8 +289,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = parabolicDistance; // We can precompute the world-space parabola normal and reuse it for the parabola plane intersects AABox sphere check diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 49a039935c..7a52cfc410 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); + QVector4D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,8 +67,9 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(std::unordered_map>& picks, +QVector4D PickCacheOptimizer::update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) { + QVector4D numIntersectionsComputed; PickCache results; const uint32_t INVALID_PICK_ID = 0; auto itr = picks.begin(); @@ -91,6 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); + numIntersectionsComputed[0]++; if (entityRes) { cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); } @@ -101,6 +103,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); + numIntersectionsComputed[1]++; if (overlayRes) { cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } @@ -111,6 +114,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) { PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick); + numIntersectionsComputed[2]++; if (avatarRes) { cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } @@ -122,6 +126,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) { PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick); + numIntersectionsComputed[3]++; if (hudRes) { cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick); } @@ -145,6 +150,7 @@ void PickCacheOptimizer::update(std::unordered_mapsecond].erase(uid); _typeMap.erase(uid); + _totalPickCounts[type->second]--; } }); } @@ -96,12 +98,12 @@ void PickManager::update() { }); bool shouldPickHUD = _shouldPickHUDOperator(); - // we pass the same expiry to both updates, but the stylus updates are relatively cheap - // and the rayPicks updae will ALWAYS update at least one ray even when there is no budget - _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); - _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); - _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); - _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + // FIXME: give each type its own expiry + // Each type will update at least one pick, regardless of the expiry + _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); + _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); } bool PickManager::isLeftHand(unsigned int uid) { diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 595c43e71d..e8d32771e0 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -58,10 +58,16 @@ public: bool getForceCoarsePicking() { return _forceCoarsePicking; } + const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } + const std::vector& getTotalPickCounts() { return _totalPickCounts; } + public slots: void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } protected: + std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; + std::vector _totalPickCounts { 0, 0, 0, 0 }; + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 22bc6b6ca7..ae3ee6e9a2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -385,9 +385,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { @@ -527,9 +527,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) {