mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 03:53:52 +02:00
sort, optimize, and timebox process avatar updates
This commit is contained in:
parent
d3dcc2b415
commit
ac180758ad
3 changed files with 83 additions and 62 deletions
|
@ -323,10 +323,13 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
PerformanceTimer perfTimer("simulate");
|
||||
{
|
||||
PROFILE_RANGE(simulation, "updateJoints");
|
||||
uint64_t start = usecTimestampNow();
|
||||
if (inView) {
|
||||
if (inView && _hasNewJointData) {
|
||||
uint64_t start = usecTimestampNow();
|
||||
_skeletonModel->getRig()->copyJointsFromJointData(_jointData);
|
||||
_skeletonModel->simulate(deltaTime, _hasNewJointData);
|
||||
_skeletonModel->simulate(deltaTime, true);
|
||||
timeProcessingJoints += usecTimestampNow() - start;
|
||||
numJointsProcessed += _jointData.size();
|
||||
|
||||
locationChanged(); // joints changed, so if there are any children, update them.
|
||||
_hasNewJointData = false;
|
||||
|
||||
|
@ -337,15 +340,11 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
Head* head = getHead();
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(getUniformScale());
|
||||
const bool useBillboard = false; // HACK
|
||||
head->simulate(deltaTime, false, useBillboard);
|
||||
head->simulate(deltaTime, false);
|
||||
} else {
|
||||
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
||||
getHead()->setPosition(getPosition());
|
||||
_skeletonModel->simulate(deltaTime, false);
|
||||
}
|
||||
timeProcessingJoints += usecTimestampNow() - start;
|
||||
numJointsProcessed += _jointData.size();
|
||||
}
|
||||
|
||||
// update animation for display name fade in/out
|
||||
|
|
|
@ -149,14 +149,11 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
lock.unlock();
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
uint64_t startTime = usecTimestampNow();
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
||||
uint64_t now = usecTimestampNow();
|
||||
ViewFrustum cameraView;
|
||||
qApp->copyDisplayViewFrustum(cameraView);
|
||||
glm::vec3 frustumCenter = cameraView.getPosition();
|
||||
|
@ -164,47 +161,56 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
const float OUT_OF_VIEW_PENALTY = -10.0;
|
||||
|
||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||
for (int32_t i = 0; i < avatarList.size(); ++i) {
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(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
|
||||
continue;
|
||||
}
|
||||
if (avatar->shouldDie()) {
|
||||
removeAvatar(avatar->getID());
|
||||
continue;
|
||||
}
|
||||
if (avatar->isDead()) {
|
||||
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;
|
||||
{
|
||||
PROFILE_RANGE(simulation, "sort");
|
||||
for (int32_t i = 0; i < avatarList.size(); ++i) {
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(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
|
||||
continue;
|
||||
}
|
||||
if (avatar->shouldDie()) {
|
||||
removeAvatar(avatar->getID());
|
||||
continue;
|
||||
}
|
||||
if (avatar->isDead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// priority = weighted linear combination of:
|
||||
// (a) apparentSize
|
||||
// (b) proximity to center of view
|
||||
// (c) time since last update
|
||||
// (d) TIME_PENALTY to help recently updated entries sort toward back
|
||||
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;
|
||||
const float TIME_PENALTY = 0.080f; // seconds
|
||||
float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY;
|
||||
float priority = 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));
|
||||
}
|
||||
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;
|
||||
// NOTE: the copy of hash and sort above takes about 300 usec
|
||||
const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec
|
||||
const uint64_t MAX_UPDATE_BUDGET = 2000; // usec
|
||||
uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET;
|
||||
uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET;
|
||||
size_t numAvatarsProcessed = sortedAvatars.size();
|
||||
while (!sortedAvatars.empty()) {
|
||||
const AvatarPriority& sortData = sortedAvatars.top();
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||
|
@ -225,12 +231,23 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
|
||||
// for avatars in view...
|
||||
const bool inView = sortData.priority > 0.5f * OUT_OF_VIEW_PENALTY;
|
||||
avatar->simulate(deltaTime, inView);
|
||||
if (expiry > usecTimestampNow()) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (now < renderExpiry) {
|
||||
// we're within budget
|
||||
bool inView = sortData.priority > 0.5f * OUT_OF_VIEW_PENALTY;
|
||||
avatar->simulate(deltaTime, inView);
|
||||
avatar->updateRenderItem(pendingChanges);
|
||||
avatar->setLastRenderUpdateTime(now);
|
||||
avatar->setLastRenderUpdateTime(startTime);
|
||||
} else if (now < maxExpiry) {
|
||||
// we've spent most of our time budget, but we still simulate() the avatar as it if were out of view
|
||||
// --> some avatars may freeze until their priority trickles up
|
||||
const bool inView = false;
|
||||
avatar->simulate(deltaTime, inView);
|
||||
} else {
|
||||
// we've spent ALL of our time budget --> bail on the rest of the avatar updates
|
||||
// --> some scale or fade animations may glitch
|
||||
// --> some avatar velocity measurements may be a little off
|
||||
break;
|
||||
}
|
||||
sortedAvatars.pop();
|
||||
}
|
||||
|
@ -238,8 +255,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
||||
//PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec",
|
||||
// { { "NumAvatarsPerSec", (float)((size() - sortedAvatars.size()) * USECS_PER_SECOND) / (float)(usecTimestampNow() - now) } });
|
||||
numAvatarsProcessed -= sortedAvatars.size();
|
||||
float numAvatarsPerSec = (float)(numAvatarsProcessed * USECS_PER_SECOND) / (float)(usecTimestampNow() - startTime);
|
||||
PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec", { { "NumAvatarsPerSec", numAvatarsPerSec } });
|
||||
PROFILE_COUNTER(simulation_avatar, "NumJointsPerSec", { { "NumJointsPerSec", Avatar::getNumJointsProcessedPerSecond() } });
|
||||
}
|
||||
|
||||
|
|
|
@ -220,15 +220,19 @@ void SkeletonModel::updateAttitude() {
|
|||
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||
// but just before head has been simulated.
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
updateAttitude();
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
if (fullUpdate) {
|
||||
updateAttitude();
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
|
||||
// let rig compute the model offset
|
||||
glm::vec3 registrationPoint;
|
||||
if (_rig->getModelRegistrationPoint(registrationPoint)) {
|
||||
setOffset(registrationPoint);
|
||||
// let rig compute the model offset
|
||||
glm::vec3 registrationPoint;
|
||||
if (_rig->getModelRegistrationPoint(registrationPoint)) {
|
||||
setOffset(registrationPoint);
|
||||
}
|
||||
} else {
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
}
|
||||
|
||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||
|
|
Loading…
Reference in a new issue