From e3634239921cc09c20096195203cd5d7114e8929 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jan 2017 14:05:25 -0800 Subject: [PATCH] compute priority of avatar and sort before update --- interface/src/avatar/Avatar.cpp | 5 +- interface/src/avatar/Avatar.h | 4 ++ interface/src/avatar/AvatarManager.cpp | 87 +++++++++++++++++++------- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 73c8a7553b..260373bf50 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -127,6 +127,7 @@ Avatar::Avatar(RigPointer rig) : _nameRectGeometryID = geometryCache->allocateID(); _leftPointerGeometryID = geometryCache->allocateID(); _rightPointerGeometryID = geometryCache->allocateID(); + _lastRenderUpdateTime = usecTimestampNow(); } Avatar::~Avatar() { @@ -320,10 +321,6 @@ void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "updateJoints"); uint64_t start = usecTimestampNow(); if (inView) { - // adebug BOOKMARK: can avoid copying duplicate joint data - // - can avoid some work when transform not changed (see SkeletonModel::simulate() and Model::setTranslation()) - // - can maybe use _hasNewFoo instead of AvatarData::_jointDataVersion - // - can maybe avoid stuff if blendShapes haven't changed _skeletonModel->getRig()->copyJointsFromJointData(_jointData); _skeletonModel->simulate(deltaTime, _hasNewJointData); locationChanged(); // joints changed, so if there are any children, update them. diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 75af330452..3276eaff47 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -177,6 +177,9 @@ public: glm::vec3 getUncachedRightPalmPosition() const; glm::quat getUncachedRightPalmRotation() const; + uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; } + void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; } + Q_INVOKABLE void setShouldDie(); public slots: @@ -259,6 +262,7 @@ protected: void ensureInScene(AvatarSharedPointer self); private: + uint64_t _lastRenderUpdateTime { 0 }; int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; int _nameRectGeometryID { 0 }; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4ba3cbdc1a..e3d3164b68 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -132,53 +132,94 @@ void AvatarManager::updateMyAvatar(float deltaTime) { Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar"); +class AvatarPriority { +public: + AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} + AvatarSharedPointer avatar; + float priority; + // NOTE: we invert the less-than operator to sort high priorities to front + bool operator<(const AvatarPriority& other) const { return priority > other.priority; } +}; + void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); - if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { return; } - lock.unlock(); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); - render::PendingChanges pendingChanges; - // simulate avatars - auto hashCopy = getHashCopy(); + auto avatarMap = getHashCopy(); + QList avatarList = avatarMap.values(); + uint64_t now = usecTimestampNow(); + ViewFrustum cameraView; + qApp->copyDisplayViewFrustum(cameraView); + glm::vec3 frustumCenter = cameraView.getPosition(); - uint64_t start = usecTimestampNow(); - AvatarHash::iterator avatarIterator = hashCopy.begin(); - while (avatarIterator != hashCopy.end()) { - auto avatar = std::static_pointer_cast(avatarIterator.value()); + const float OUT_OF_VIEW_PENALTY = -10.0; + std::priority_queue sortedAvatars; + for (int32_t i = 0; i < avatarList.size(); ++i) { + const auto& avatar = std::static_pointer_cast(avatarList.at(i)); if (avatar == _myAvatar || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars - ++avatarIterator; - } else if (avatar->shouldDie()) { - removeAvatar(avatarIterator.key()); - ++avatarIterator; - } else { - avatar->ensureInScene(avatar); - const bool inView = true; // HACK - avatar->simulate(deltaTime, inView); - ++avatarIterator; - - avatar->updateRenderItem(pendingChanges); + continue; } + if (avatar->shouldDie()) { + removeAvatar(avatar->getID()); + continue; + } + + // priority = weighted linear combination of: + // (a) apparentSize + // (b) proximity to center of view, and + // (c) time since last update + glm::vec3 avatarPosition = avatar->getPosition(); + glm::vec3 offset = avatarPosition - frustumCenter; + float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero + float radius = avatar->getBoundingRadius(); + const glm::vec3& forward = cameraView.getDirection(); + float apparentSize = radius / distance; + float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance; + float age = (float)(now - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND); + float priority = 2.0f * apparentSize + 0.25f * cosineAngle + age; + + // decrement priority of avatars outside keyhole + if (distance > cameraView.getCenterRadius()) { + if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { + priority += OUT_OF_VIEW_PENALTY; + } + } + sortedAvatars.push(AvatarPriority(avatar, priority)); + } + + render::PendingChanges pendingChanges; + const uint64_t AVATAR_RENDER_UPDATE_TIME_BUDGET = 2 * USECS_PER_MSEC; + uint64_t expiry = now + AVATAR_RENDER_UPDATE_TIME_BUDGET; + while (!sortedAvatars.empty()) { + const AvatarPriority& sortData = sortedAvatars.top(); + const auto& avatar = std::static_pointer_cast(sortData.avatar); + avatar->ensureInScene(avatar); + const bool inView = sortData.priority > 0.5f * OUT_OF_VIEW_PENALTY; + avatar->simulate(deltaTime, inView); + if (expiry > usecTimestampNow()) { + avatar->updateRenderItem(pendingChanges); + avatar->setLastRenderUpdateTime(now); + } + sortedAvatars.pop(); } qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges); - // simulate avatar fades simulateAvatarFades(deltaTime); - PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", - { { "NumAvatarsPerSec", (float)(size() * USECS_PER_SECOND) / (float)(usecTimestampNow() - start) } }); + //PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", + // { { "NumAvatarsPerSec", (float)((size() - sortedAvatars.size()) * USECS_PER_SECOND) / (float)(usecTimestampNow() - now) } }); PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } }); }