mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 15:49:24 +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");
|
PerformanceTimer perfTimer("simulate");
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation, "updateJoints");
|
PROFILE_RANGE(simulation, "updateJoints");
|
||||||
uint64_t start = usecTimestampNow();
|
if (inView && _hasNewJointData) {
|
||||||
if (inView) {
|
uint64_t start = usecTimestampNow();
|
||||||
_skeletonModel->getRig()->copyJointsFromJointData(_jointData);
|
_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.
|
locationChanged(); // joints changed, so if there are any children, update them.
|
||||||
_hasNewJointData = false;
|
_hasNewJointData = false;
|
||||||
|
|
||||||
|
@ -337,15 +340,11 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
||||||
Head* head = getHead();
|
Head* head = getHead();
|
||||||
head->setPosition(headPosition);
|
head->setPosition(headPosition);
|
||||||
head->setScale(getUniformScale());
|
head->setScale(getUniformScale());
|
||||||
const bool useBillboard = false; // HACK
|
head->simulate(deltaTime, false);
|
||||||
head->simulate(deltaTime, false, useBillboard);
|
|
||||||
} else {
|
} else {
|
||||||
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
// 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);
|
_skeletonModel->simulate(deltaTime, false);
|
||||||
}
|
}
|
||||||
timeProcessingJoints += usecTimestampNow() - start;
|
|
||||||
numJointsProcessed += _jointData.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update animation for display name fade in/out
|
// update animation for display name fade in/out
|
||||||
|
|
|
@ -149,14 +149,11 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
}
|
}
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
|
||||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
|
||||||
|
|
||||||
PerformanceTimer perfTimer("otherAvatars");
|
PerformanceTimer perfTimer("otherAvatars");
|
||||||
|
uint64_t startTime = usecTimestampNow();
|
||||||
|
|
||||||
auto avatarMap = getHashCopy();
|
auto avatarMap = getHashCopy();
|
||||||
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
ViewFrustum cameraView;
|
ViewFrustum cameraView;
|
||||||
qApp->copyDisplayViewFrustum(cameraView);
|
qApp->copyDisplayViewFrustum(cameraView);
|
||||||
glm::vec3 frustumCenter = cameraView.getPosition();
|
glm::vec3 frustumCenter = cameraView.getPosition();
|
||||||
|
@ -164,47 +161,56 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
const float OUT_OF_VIEW_PENALTY = -10.0;
|
const float OUT_OF_VIEW_PENALTY = -10.0;
|
||||||
|
|
||||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
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));
|
PROFILE_RANGE(simulation, "sort");
|
||||||
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
for (int32_t i = 0; i < avatarList.size(); ++i) {
|
||||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
const auto& avatar = std::static_pointer_cast<Avatar>(avatarList.at(i));
|
||||||
// DO NOT update or fade out uninitialized Avatars
|
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
||||||
continue;
|
// 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->shouldDie()) {
|
continue;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
render::PendingChanges pendingChanges;
|
||||||
const uint64_t AVATAR_RENDER_UPDATE_TIME_BUDGET = 2 * USECS_PER_MSEC;
|
// NOTE: the copy of hash and sort above takes about 300 usec
|
||||||
uint64_t expiry = now + AVATAR_RENDER_UPDATE_TIME_BUDGET;
|
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()) {
|
while (!sortedAvatars.empty()) {
|
||||||
const AvatarPriority& sortData = sortedAvatars.top();
|
const AvatarPriority& sortData = sortedAvatars.top();
|
||||||
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
|
||||||
|
@ -225,12 +231,23 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
}
|
}
|
||||||
avatar->animateScaleChanges(deltaTime);
|
avatar->animateScaleChanges(deltaTime);
|
||||||
|
|
||||||
// for avatars in view...
|
uint64_t now = usecTimestampNow();
|
||||||
const bool inView = sortData.priority > 0.5f * OUT_OF_VIEW_PENALTY;
|
if (now < renderExpiry) {
|
||||||
avatar->simulate(deltaTime, inView);
|
// we're within budget
|
||||||
if (expiry > usecTimestampNow()) {
|
bool inView = sortData.priority > 0.5f * OUT_OF_VIEW_PENALTY;
|
||||||
|
avatar->simulate(deltaTime, inView);
|
||||||
avatar->updateRenderItem(pendingChanges);
|
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();
|
sortedAvatars.pop();
|
||||||
}
|
}
|
||||||
|
@ -238,8 +255,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
|
|
||||||
simulateAvatarFades(deltaTime);
|
simulateAvatarFades(deltaTime);
|
||||||
|
|
||||||
//PROFILE_COUNTER(simulation_avatar, "NumAvatarsPerSec",
|
numAvatarsProcessed -= sortedAvatars.size();
|
||||||
// { { "NumAvatarsPerSec", (float)((size() - sortedAvatars.size()) * USECS_PER_SECOND) / (float)(usecTimestampNow() - now) } });
|
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() } });
|
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),
|
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
|
||||||
// but just before head has been simulated.
|
// but just before head has been simulated.
|
||||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
updateAttitude();
|
if (fullUpdate) {
|
||||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
updateAttitude();
|
||||||
|
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||||
|
|
||||||
Model::simulate(deltaTime, fullUpdate);
|
Model::simulate(deltaTime, fullUpdate);
|
||||||
|
|
||||||
// let rig compute the model offset
|
// let rig compute the model offset
|
||||||
glm::vec3 registrationPoint;
|
glm::vec3 registrationPoint;
|
||||||
if (_rig->getModelRegistrationPoint(registrationPoint)) {
|
if (_rig->getModelRegistrationPoint(registrationPoint)) {
|
||||||
setOffset(registrationPoint);
|
setOffset(registrationPoint);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Model::simulate(deltaTime, fullUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||||
|
|
Loading…
Reference in a new issue