// *************************************************************************** void CTransformShape::traverseLoadBalancingPass0() { CLoadBalancingTrav &loadTrav= getOwnerScene()->getLoadBalancingTrav(); CSkeletonModel *skeleton= getSkeletonModel(); // World Model position const CVector *modelPos; // If this isntance is binded or skinned to a skeleton, take the world matrix of this one as // center for LoadBalancing Resolution. if(skeleton) { // Take the root bone of the skeleton as reference (bone 0) // And so get our position. modelPos= &skeleton->Bones[0].getWorldMatrix().getPos(); } else { // get our position from modelPos= &getWorldMatrix().getPos(); } // Then compute distance from camera. float modelDist= ( loadTrav.CamPos - *modelPos).norm(); // Get the number of triangles this model use now. _FaceCount= getNumTriangles(modelDist); _LoadBalancingGroup->addNbFacesPass0(_FaceCount); }
void AvatarManager::updateOtherAvatars(float deltaTime) { { // lock the hash for read to check the size QReadLocker lock(&_hashLock); if (_avatarHash.size() < 2) { return; } } PerformanceTimer perfTimer("otherAvatars"); class SortableAvatar: public PrioritySortUtil::Sortable { public: SortableAvatar() = delete; SortableAvatar(const std::shared_ptr<Avatar>& avatar) : _avatar(avatar) {} glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } float getRadius() const override { return _avatar->getBoundingRadius(); } uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); } std::shared_ptr<Avatar> getAvatar() const { return _avatar; } private: std::shared_ptr<Avatar> _avatar; }; auto avatarMap = getHashCopy(); const auto& views = qApp->getConicalViews(); // Prepare 2 queues for heros and for crowd avatars using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>; // Keep two independent queues, one for heroes and one for the riff-raff. enum PriorityVariants { kHero = 0, kNonHero, NumVariants }; AvatarPriorityQueue avatarPriorityQueues[NumVariants] = { { views, AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge }, { views, AvatarData::_avatarSortCoefficientSize, AvatarData::_avatarSortCoefficientCenter, AvatarData::_avatarSortCoefficientAge } }; // Reserve space //avatarPriorityQueues[kHero].reserve(10); // just few avatarPriorityQueues[kNonHero].reserve(avatarMap.size() - 1); // don't include MyAvatar // Build vector and compute priorities auto nodeList = DependencyManager::get<NodeList>(); AvatarHash::iterator itr = avatarMap.begin(); while (itr != avatarMap.end()) { auto avatar = std::static_pointer_cast<Avatar>(*itr); // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) { if (avatar->getHasPriority()) { avatarPriorityQueues[kHero].push(SortableAvatar(avatar)); } else { avatarPriorityQueues[kNonHero].push(SortableAvatar(avatar)); } } ++itr; } _numHeroAvatars = (int)avatarPriorityQueues[kHero].size(); // process in sorted order uint64_t startTime = usecTimestampNow(); const uint64_t MAX_UPDATE_HEROS_TIME_BUDGET = uint64_t(0.8 * MAX_UPDATE_AVATARS_TIME_BUDGET); uint64_t updatePriorityExpiries[NumVariants] = { startTime + MAX_UPDATE_HEROS_TIME_BUDGET, startTime + MAX_UPDATE_AVATARS_TIME_BUDGET }; int numHerosUpdated = 0; int numAvatarsUpdated = 0; int numAvatarsNotUpdated = 0; render::Transaction renderTransaction; workload::Transaction workloadTransaction; for (int p = kHero; p < NumVariants; p++) { auto& priorityQueue = avatarPriorityQueues[p]; // Sorting the current queue HERE as part of the measured timing. const auto& sortedAvatarVector = priorityQueue.getSortedVector(); auto passExpiry = updatePriorityExpiries[p]; for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) { const SortableAvatar& sortData = *it; const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar()); if (!avatar->_isClientAvatar) { avatar->setIsClientAvatar(true); } // TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update if (avatar->getSkeletonModel()->isLoaded()) { // remove the orb if it is there avatar->removeOrb(); if (avatar->needsPhysicsUpdate()) { _otherAvatarsToChangeInPhysics.insert(avatar); } } else { avatar->updateOrbPosition(); } // for ALL avatars... if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } avatar->animateScaleChanges(deltaTime); uint64_t now = usecTimestampNow(); if (now < passExpiry) { // we're within budget bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig); if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { avatar->_transit.reset(); avatar->setIsNewAvatar(false); } avatar->simulate(deltaTime, inView); if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) { _myAvatar->addAvatarHandsToFlow(avatar); } if (_drawOtherAvatarSkeletons) { avatar->debugJointData(); } avatar->setEnableMeshVisible(!_drawOtherAvatarSkeletons); avatar->updateRenderItem(renderTransaction); avatar->updateSpaceProxy(workloadTransaction); avatar->setLastRenderUpdateTime(startTime); } else { // we've spent our time budget for this priority bucket // let's deal with the reminding avatars if this pass and BREAK from the for loop if (p == kHero) { // Hero, // --> put them back in the non hero queue auto& crowdQueue = avatarPriorityQueues[kNonHero]; while (it != sortedAvatarVector.end()) { crowdQueue.push(SortableAvatar((*it).getAvatar())); ++it; } } else { // Non Hero // --> bail on the rest of the avatar updates // --> more avatars may freeze until their priority trickles up // --> some scale animations may glitch // --> some avatar velocity measurements may be a little off // no time to simulate, but we take the time to count how many were tragically missed numAvatarsNotUpdated = sortedAvatarVector.end() - it; } // We had to cut short this pass, we must break out of the for loop here break; } } if (p == kHero) { numHerosUpdated = numAvatarsUpdated; } } if (_shouldRender) { qApp->getMain3DScene()->enqueueTransaction(renderTransaction); } _space->enqueueTransaction(workloadTransaction); _numAvatarsUpdated = numAvatarsUpdated; _numAvatarsNotUpdated = numAvatarsNotUpdated; _numHeroAvatarsUpdated = numHerosUpdated; _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC; }