Synchronize looping animations that start at the same moment.

This commit is contained in:
Aleric Inglewood
2013-12-25 03:14:44 +01:00
parent 1bcb6ad20d
commit 1c8876cead
11 changed files with 484 additions and 87 deletions

View File

@@ -194,11 +194,26 @@ void LLCharacter::updateMotions(e_update_t update_type)
{
if (update_type == HIDDEN_UPDATE)
{
//<singu>
// Keep updating avatars that have at least one motion that is synchronized with a still running motion.
// This call tells the other controllers that we are in principle hidden.
// It returns false if we need to keep updating anyway.
if (!mMotionController.hidden(true))
{
mMotionController.updateMotions(LLCharacter::NORMAL_UPDATE);
return;
}
//</singu>
LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION);
mMotionController.updateMotionsMinimal();
}
else
{
//<singu>
// This call tells the other controllers that we are visible and that they need
// to keep updating if they are synchronized with us, even if they are hidden.
mMotionController.hidden(false);
//</singu>
LLFastTimer t(FTM_UPDATE_ANIMATION);
// unpause if the number of outstanding pause requests has dropped to the initial one
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)

View File

@@ -818,7 +818,44 @@ void LLKeyframeMotion::onDeactivate()
//-----------------------------------------------------------------------------
// setStopTime()
//-----------------------------------------------------------------------------
// time is in seconds since character creation
//
// Consider a looping animation of 20 frames, where the loop in point is at 3 frames
// and the loop out point at 16 frames:
//
// The first 3 frames of the animation would be the "loop in" animation.
// The last 4 frames of the animation would be the "loop out" animation.
// Frames 4 through 15 would be the looping animation frames.
//
// If the animation would not be looping, all frames would just be played once sequentially:
//
// mActivationTimestamp -.
// v
// 0 3 15 16 20
// | | \| |
// ---------------------
// <--> <-- mLoopInPoint (relative to mActivationTimestamp)
// <--------------> <-- mLoopOutPoint (relative to mActivationTimestamp)
// <----mDuration------>
//
// When looping the animation would repeat frames 3 to 16 (loop) a few times, for example:
//
// 0 3 15 3 15 3 15 3 15 16 20
// | | loop 1 \| loop 2 \| loop 3 \| loop 4 \| |
// ------------------------------------------------------------
//LOOP^ ^ LOOP
// IN | <----->| OUT
// start_loop_time loop_fraction_time-' time
//
// The time at which the animation is started corresponds to frame 0 and is stored
// in mActivationTimestamp (in seconds since character creation).
//
// If setStopTime() is called with a time somewhere inside loop 4,
// then 'loop_fraction_time' is the time from the beginning of
// loop 4 till 'time'. Thus 'time - loop_fraction_time' is the first
// frame of loop 4, and '(time - loop_fraction_time) +
// (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint)'
// would correspond to frame 20.
//
void LLKeyframeMotion::setStopTime(F32 time)
{
LLMotion::setStopTime(time);
@@ -836,6 +873,8 @@ void LLKeyframeMotion::setStopTime(F32 time)
loop_fraction_time = fmod(time - start_loop_time,
mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
}
// This sets mStopTimestamp to the time that corresponds to the end of the animation (ie, frame 20 in the above example)
// minus the ease out duration, so that the animation eases out during the loop out and finishes exactly at the end.
mStopTimestamp = llmax(time,
(time - loop_fraction_time) + (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint) - getEaseOutDuration());
}

View File

@@ -39,6 +39,122 @@
#include "llcriticaldamp.h"
#include "llmotioncontroller.h"
//<singu>
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// AISyncClientMotion class
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
AISyncKey* AISyncClientMotion::createSyncKey(AISyncKey const* from_key) const
{
// The const cast is needed because getDuration() is non-const while it should have been.
AISyncClientMotion* self = const_cast<AISyncClientMotion*>(this);
// Only synchronize motions with the same duration and loop value.
return new AISyncKeyMotion(from_key, self->getDuration(), self->getLoop());
}
void AISyncClientMotion::aisync_loading(void)
{
// Register the motion for (possible) synchronization: this marks the time at which is should have started.
unregister_client(); // In case it is already registered. Getting here means we are being (re)started now, we need to synchronize with other motions that start now.
register_client();
}
void AISyncClientMotion::aisync_loaded(void)
{
AISyncServer* server = this->server();
if (!server)
{
// Already expired without being synchronized (no other motion was started at the same time).
return;
}
AISyncKey const& key = server->key(); // The allocation of this is owned by server.
// There is no need to resync if there was not another motion started at the same time and the key already expired.
bool need_resync = !(server->never_synced() && key.expired());
AISyncKey* new_key;
if (need_resync)
{
// Create a new key using the old start time.
new_key = createSyncKey(&key);
}
server->remove(this); // This resets mServer and might even delete server.
if (need_resync)
{
// Add the client to another server (based on the new key). This takes ownership of the key allocation.
AISyncServerMap::instance().register_client(this, new_key);
}
}
F32 LLMotion::getRuntime(void) const
{
llassert(mActive);
return mController->getAnimTime() - mActivationTimestamp;
}
F32 LLMotion::getAnimTime(void) const
{
return mController->getAnimTime();
}
F32 LLMotion::syncActivationTime(F32 time)
{
AISyncServer* server = this->server();
if (!server)
{
register_client();
server = this->server();
}
AISyncServer::client_list_t const& clients = server->getClients();
if (clients.size() > 1)
{
// Look for the client with the smallest runtime.
AISyncClientMotion* motion_with_smallest_runtime = NULL;
F32 runtime = 1e10;
// Run over all motions in this to be synchronized group.
for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client)
{
if ((client->mReadyEvents & 2)) // Is this motion active? Motions that aren't loaded yet are not active.
{
// Currently, if event 2 is set then this is an LLMotion.
llassert(dynamic_cast<AISyncClientMotion*>(client->mClientPtr));
AISyncClientMotion* motion = static_cast<AISyncClientMotion*>(client->mClientPtr);
// Deactivated motions should have been deregistered, certainly not have event 2 set.
llassert(static_cast<LLMotion*>(motion)->isActive());
if (motion->getRuntime() < runtime)
{
// This is a bit fuzzy since theoretically the runtime of all active motions in the list should be the same.
// Just use the smallest value to get rid of some randomness. We might even synchronizing with ourselves
// in which case 'time' would be set to a value such that mActivationTimestamp won't change.
// In practise however, this list will contain only two clients: this, being inactive, and our partner.
runtime = motion->getRuntime();
motion_with_smallest_runtime = motion;
}
}
}
//-----------------------------------------------------------------------------------------
// Here is where the actual synchronization takes place.
// Current we only synchronize looped motions.
if (getLoop())
{
if (motion_with_smallest_runtime)
{
// Pretend the motion was started in the past at the same time as the other motion(s).
time = getAnimTime() - runtime;
}
}
//-----------------------------------------------------------------------------------------
}
return time;
}
void AISyncClientMotion::deregistered(void)
{
mReadyEvents = 0;
}
//</singu>
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLMotion class
@@ -149,6 +265,19 @@ void LLMotion::activate(F32 time)
{
mActivationTimestamp = time;
mStopped = FALSE;
//<singu>
if (mController && !mController->syncing_disabled()) // Avoid being registered when syncing is disabled for this motion.
{
if (mActive)
{
// If the motion is already active then we are being restarted.
// Unregister it first (if it is registered) so that the call to ready will cause it to be registered
// and be synchronized with other motions that are started at this moment.
unregister_client();
}
ready(6, 2 | (mController->isHidden() ? 0 : 4)); // Signal that mActivationTimestamp is set/valid (2), and that this server has a visible motion (4) (or not).
}
//</singu>
mActive = TRUE;
onActivate();
}
@@ -161,6 +290,14 @@ void LLMotion::deactivate()
mActive = FALSE;
mPose.setWeight(0.f);
//<singu>
if (server()) // Only when this motion is already registered.
{
ready(6, 0); // Signal that mActivationTimestamp is no longer valid.
unregister_client(); // No longer running, so no longer a part of this sync group.
}
//</singu>
if (mDeactivateCallback)
{
(*mDeactivateCallback)(mDeactivateCallbackUserData);

View File

@@ -38,6 +38,7 @@
//-----------------------------------------------------------------------------
#include <string>
#include "aisyncclient.h"
#include "llerror.h"
#include "llpose.h"
#include "lluuid.h"
@@ -45,10 +46,77 @@
class LLCharacter;
class LLMotionController;
//-----------------------------------------------------------------------------
// class AISync* stuff
//-----------------------------------------------------------------------------
class AISyncKeyMotion : public AISyncKey
{
private:
F32 mDuration;
bool mLoop;
public:
AISyncKeyMotion(AISyncKey const* from_key, F32 duration, bool loop) : AISyncKey(from_key), mDuration(duration), mLoop(loop) { }
// Virtual functions of AISyncKey.
public:
/*virtual*/ synckeytype_t getkeytype(void) const
{
// Return a unique identifier for this class, where the low 8 bits represent the syncgroup.
return synckeytype_motion;
}
/*virtual*/ bool equals(AISyncKey const& key) const
{
switch (key.getkeytype())
{
case synckeytype_motion:
{
// The other key is of the same type.
AISyncKeyMotion const& motion_key = static_cast<AISyncKeyMotion const&>(key);
return mLoop == motion_key.mLoop && is_approx_equal(mDuration, motion_key.mDuration);
}
default:
// The keys must be in the same syncgroup.
break;
}
return false;
}
};
class AISyncClientMotion : public AISyncClient
{
protected:
// Make sure that clients that are destroyed are first unregistered.
// This is needed, for example, when loading fails or when excess motions are being purged.
/*virtual*/ ~AISyncClientMotion() { unregister_client(); }
// AISyncClient events.
/*virtual*/ AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const;
/*virtual*/ void event1_ready(void) { }
/*virtual*/ void event1_not_ready(void) { }
/*virtual*/ void deregistered(void);
protected:
// This is called when the server sent us a message that it wants us to play this animation, but we can't because it isn't fully downloaded yet.
void aisync_loading(void);
// This is called when that motion is successfully loaded and it has to be re-registered because now the duration etc is known.
void aisync_loaded(void);
public:
// Virtual functions of AISyncClientMotion.
// These are defined by classes derived from LLMotion (which is derived from this class).
virtual BOOL getLoop() = 0;
virtual F32 getDuration() = 0;
virtual F32 getAnimTime(void) const = 0;
virtual F32 getRuntime(void) const = 0;
};
//-----------------------------------------------------------------------------
// class LLMotion
//-----------------------------------------------------------------------------
class LLMotion
class LLMotion : public AISyncClientMotion
{
friend class LLMotionController;
@@ -115,7 +183,22 @@ protected:
BOOL isActive() { return mActive; }
public:
void activate(F32 time);
//<singu>
// Returns the time that this motion has been running.
virtual F32 getRuntime(void) const;
// Return the current time (in seconds since creation of the controller).
virtual F32 getAnimTime(void) const;
// This is called when a motion is to be activated, but might need synchronization.
// It adjusts the start time to match that of other motions in the same synchronization group that were already started.
F32 syncActivationTime(F32 time);
// Accessor.
LLMotionController* getController(void) const { return mController; }
//</singu>
public:
//-------------------------------------------------------------------------
// animation callbacks to be implemented by subclasses

View File

@@ -130,6 +130,9 @@ LLMotionController::LLMotionController()
mTimeFactor(sCurrentTimeFactor),
mCharacter(NULL),
mActiveMask(0),
mDisableSyncing(0),
mHidden(false),
mHaveVisibleSyncedMotions(false),
mPrevTimerElapsed(0.f),
mAnimTime(0.f),
mLastTime(0.0f),
@@ -173,6 +176,10 @@ void LLMotionController::deleteAllMotions()
mActiveMask = 0;
for_each(mDeprecatedMotions.begin(), mDeprecatedMotions.end(), DeletePointer());
mDeprecatedMotions.clear();
for (motion_map_t::iterator iter = mAllMotions.begin(); iter != mAllMotions.end(); ++iter)
{
iter->second->unregister_client();
}
//</singu>
for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer());
mAllMotions.clear();
@@ -437,7 +444,19 @@ BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
}
// llinfos << "Starting motion " << name << llendl;
return activateMotionInstance(motion, mAnimTime - start_offset);
//<singu>
F32 start_time = mAnimTime - start_offset;
if (!mDisableSyncing)
{
start_time = motion->syncActivationTime(start_time);
}
++mDisableSyncing;
//</singu>
BOOL res = activateMotionInstance(motion, start_time);
//<singu>
--mDisableSyncing;
//</singu>
return res;
}
@@ -793,7 +812,19 @@ void LLMotionController::updateLoadingMotions()
// this motion should be playing
if (!motionp->isStopped())
{
activateMotionInstance(motionp, mAnimTime);
//<singu>
F32 start_time = mAnimTime;
if (!mDisableSyncing)
{
motionp->aisync_loaded();
start_time = motionp->syncActivationTime(start_time);
}
++mDisableSyncing;
//</singu>
activateMotionInstance(motionp, start_time);
//<singu>
--mDisableSyncing;
//</singu>
}
}
else if (status == LLMotion::STATUS_FAILURE)
@@ -806,6 +837,10 @@ void LLMotionController::updateLoadingMotions()
// check for it's existence there.
llassert(mDeprecatedMotions.find(motionp) == mDeprecatedMotions.end());
mAllMotions.erase(motionp->getID());
//<singu>
// Make sure we're not registered anymore.
motionp->unregister_client();
//</singu>
delete motionp;
}
}
@@ -933,6 +968,12 @@ BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time)
if (mLoadingMotions.find(motion) != mLoadingMotions.end())
{
//<singu>
if (!syncing_disabled())
{
motion->aisync_loading();
}
//</singu>
// we want to start this motion, but we can't yet, so flag it as started
motion->setStopped(FALSE);
// report pending animations as activated
@@ -1094,9 +1135,9 @@ void LLMotionController::deactivateAllMotions()
{
// Singu note: this must run over mActiveMotions: other motions are not active,
// and running over mAllMotions will miss the ones in mDeprecatedMotions.
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end();)
{
deactivateMotionInstance(*iter);
deactivateMotionInstance(*iter++); // This might invalidate iter by erasing it from mActiveMotions.
}
}
@@ -1126,13 +1167,98 @@ void LLMotionController::flushAllMotions()
mCharacter->removeAnimationData("Hand Pose");
// restart motions
//<singu>
// Because we called motionp->deactivate() above, instead of deactivateMotionInstance(),
// prevent calling AISyncClientMotion::activateInstance in startMotion below.
disable_syncing();
//</singu>
for (std::vector<std::pair<LLUUID,F32> >::iterator iter = active_motions.begin();
iter != active_motions.end(); ++iter)
{
startMotion(iter->first, iter->second);
}
//<singu>
enable_syncing();
//</singu>
}
//<singu>
//-----------------------------------------------------------------------------
// toggle_hidden()
//-----------------------------------------------------------------------------
void LLMotionController::toggle_hidden(void)
{
mHaveVisibleSyncedMotions = mHidden; // Default is false if we just became invisible (otherwise this value isn't used).
mHidden = !mHidden;
synceventset_t const visible = mHidden ? 0 : 4;
// Run over all motions.
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
{
LLMotion* motionp = *iter;
AISyncServer* server = motionp->server();
if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active.
{
bool visible_before = server->events_with_at_least_one_client_ready() & 4;
server->ready(4, visible, motionp); // Mark that now we are visible or no longer visible.
bool visible_after = server->events_with_at_least_one_client_ready() & 4;
if (visible_after) // Are there any synchronized motions (left) that ARE visible?
{
mHaveVisibleSyncedMotions = true;
}
if (visible_before != visible_after)
{
// The group as a whole now might need to change whether or not it is animated.
AISyncServer::client_list_t const& clients = server->getClients();
for (AISyncServer::client_list_t::const_iterator client = clients.begin(); client != clients.end(); ++client)
{
LLMotion* motion = dynamic_cast<LLMotion*>(client->mClientPtr);
if (!motion)
{
continue;
}
LLMotionController* controller = motion->getController();
if (controller == this)
{
continue;
}
if (visible_after)
{
// Us becoming visible means that all synchronized avatars need to be animated again too.
controller->setHaveVisibleSyncedMotions();
}
else
{
// Us becoming hidden means that all synchronized avatars might stop animating.
controller->refresh_hidden(); // It is extremely unlikely, but harmless, to call this twice on the same controller.
}
}
}
}
}
}
void LLMotionController::refresh_hidden(void)
{
mHaveVisibleSyncedMotions = !mHidden;
// Run over all motions.
for (motion_list_t::iterator iter = mActiveMotions.begin(); iter != mActiveMotions.end(); ++iter)
{
LLMotion* motionp = *iter;
AISyncServer* server = motionp->server();
if (server && !server->never_synced() && (motionp->mReadyEvents & 2)) // Skip motions that aren't synchronized at all or that are not active.
{
bool visible_after = server->events_with_at_least_one_client_ready() & 4;
if (visible_after) // Are there any synchronized motions (left) that ARE visible?
{
mHaveVisibleSyncedMotions = true;
}
}
}
}
//</singu>
//-----------------------------------------------------------------------------
// pause()
//-----------------------------------------------------------------------------

View File

@@ -150,11 +150,11 @@ public:
//Flush is a liar.
void deactivateAllMotions();
//<edit>
//<singu>
void activated(U32 bit) { mActiveMask |= bit; }
void deactivated(U32 bit) { mActiveMask &= ~bit; }
bool isactive(U32 bit) const { return (mActiveMask & bit) != 0; }
//</edit>
//</singu>
// pause and continue all motions
void pauseAllMotions();
@@ -186,7 +186,10 @@ protected:
// internal operations act on motion instances directly
// as there can be duplicate motions per id during blending overlap
void deleteAllMotions();
// singu: LLMotion needs access to activateMotionInstance.
public:
BOOL activateMotionInstance(LLMotion *motion, F32 time);
protected:
BOOL deactivateMotionInstance(LLMotion *motion);
void deprecateMotionInstance(LLMotion* motion);
BOOL stopMotionInstance(LLMotion *motion, BOOL stop_imemdiate);
@@ -227,9 +230,12 @@ protected:
motion_list_t mActiveMotions;
motion_set_t mDeprecatedMotions;
//<edit>
//<singu>
U32 mActiveMask;
//</edit>
int mDisableSyncing; // Set while LLMotion::onActivate (and onDeactivate) are called for this controller.
bool mHidden; // The value of the last call to hidden().
bool mHaveVisibleSyncedMotions; // Set when we are synchronized with one or more motions of a controller that is not hidden.
//</singu>
LLFrameTimer mTimer;
F32 mPrevTimerElapsed;
F32 mAnimTime;
@@ -242,6 +248,26 @@ protected:
F32 mLastInterp;
U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS];
//<singu>
public:
// Internal administration for AISync.
void disable_syncing(void) { mDisableSyncing += 100; }
void enable_syncing(void) { mDisableSyncing -= 100; }
bool syncing_disabled(void) const { return mDisableSyncing >= 100; }
// Accessors needed for synchronization.
F32 getAnimTime(void) const { return mAnimTime; }
bool isHidden(void) const { return mHidden; }
// Called often. Should return false if we still need to keep updating our motions even if we're not visible.
bool hidden(bool not_visible) { if (mHidden != not_visible) toggle_hidden(); return !mHaveVisibleSyncedMotions; }
private:
void toggle_hidden(void);
void refresh_hidden(void);
void setHaveVisibleSyncedMotions(void) { mHaveVisibleSyncedMotions = true; }
//</singu>
};
//-----------------------------------------------------------------------------

View File

@@ -126,20 +126,13 @@ void print_clients(AISyncServer const* server, AISyncServer::client_list_t const
}
#endif
void AISyncServerMap::register_client(AISyncClient* client)
void AISyncServerMap::register_client(AISyncClient* client, AISyncKey* new_key)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "AISyncServerMap::register_client(" << client << ")");
#endif
// client must always be a new client that has to be stored somewhere.
llassert(client->server() == NULL);
// Obviously the client can't be ready for anything when it isn't registered yet.
llassert(!client->mReadyEvents);
// First we need its sync key.
AISyncKey* new_key = client->createSyncKey();
// Find if a server with this key already exists.
AISyncServer* server = NULL;
for (server_list_t::iterator iter = mServers.begin(); iter != mServers.end();)
@@ -183,7 +176,6 @@ void AISyncServerMap::register_client(AISyncClient* client)
server = new AISyncServer(new_key);
// Add it to mServers, before the last server that is younger then the new key.
server_list_t::iterator where = mServers.end(); // Insert the new server before 'where',
#if 0 // This is probably not necessary.
server_list_t::iterator new_where = where;
while (where != mServers.begin()) // unless there exists a server before that
{
@@ -194,10 +186,6 @@ void AISyncServerMap::register_client(AISyncClient* client)
}
where = new_where; // then insert it before that element (etc).
}
#elif defined(SHOW_ASSERT)
server_list_t::iterator new_where = where;
llassert(where == mServers.begin() || new_key->getCreationTime() >= (*--new_where)->key().getCreationTime());
#endif
// This method causes a single call to intrusive_ptr_add_ref and none to intrusive_ptr_release.
server_ptr_t server_ptr = server;
mServers.insert(where, server_ptr_t())->swap(server_ptr);
@@ -231,10 +219,6 @@ void AISyncServer::sanity_check(void) const
void AISyncServer::add(AISyncClient* client)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "AISyncServer::add(" << client << "), with this = " << this);
print_clients(this, getClients());
#endif
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
@@ -263,16 +247,10 @@ void AISyncServer::add(AISyncClient* client)
void AISyncServer::remove(AISyncClient* client)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "AISyncServer::remove(" << client << "), with this = " << this);
print_clients(this, getClients());
#endif
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
// A client may only be unregistered after it was marked not-ready for all events.
llassert(!client->mReadyEvents);
client_list_t::iterator client_iter = mClients.begin();
synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready.
synceventset_t remaining_pending_events = 0; // At least one client is ready (waiting for the other clients thus).
@@ -292,15 +270,14 @@ void AISyncServer::remove(AISyncClient* client)
}
llassert(found_client != mClients.end());
// This must be the same as client->mReadyEvents.
llassert(!found_client->mReadyEvents);
llassert(found_client->mReadyEvents == client->mReadyEvents);
mClients.erase(found_client);
client->mServer.reset();
synceventset_t old_ready_events = mReadyEvents;
mReadyEvents = remaining_ready_events;
// Since client->mReadyEvents is zero, this should be the same.
llassert(mPendingEvents == remaining_pending_events);
mPendingEvents = remaining_pending_events;
trigger(old_ready_events);
client->mServer.reset();
client->deregistered();
#ifdef SYNC_TESTSUITE
sanity_check();
@@ -309,10 +286,6 @@ void AISyncServer::remove(AISyncClient* client)
void AISyncServer::unregister_last_client(void)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "AISyncServer::unregister_last_client()");
print_clients(this, getClients());
#endif
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
@@ -333,22 +306,6 @@ void AISyncServer::unregister_last_client(void)
#endif
}
synceventset_t AISyncServer::events_with_all_clients_ready(void) const
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
return mReadyEvents;
}
synceventset_t AISyncServer::events_with_at_least_one_client_ready(void) const
{
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
return mPendingEvents;
}
void AISyncServer::trigger(synceventset_t old_ready_events)
{
// If event 1 changed, informat all clients about it.
@@ -368,22 +325,14 @@ void AISyncServer::trigger(synceventset_t old_ready_events)
}
}
bool AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client)
void AISyncServer::ready(synceventset_t events, synceventset_t yesno, AISyncClient* client)
{
#ifdef DEBUG_SYNCOUTPUT
DoutEntering(dc::notice, "AISyncServer::ready(" << SyncEventSet(events) << ", " << SyncEventSet(yesno) << ", " << client << ")");
print_clients(this, getClients());
#endif
#ifdef SYNC_TESTSUITE
sanity_check();
#endif
synceventset_t added_events = events & yesno;
synceventset_t removed_events = events & ~yesno;
// May not add events that are already ready.
llassert(!(client->mReadyEvents & added_events));
// Cannot remove events that weren't ready.
llassert((client->mReadyEvents & removed_events) == removed_events);
// Run over all clients to find the client and calculate the current state.
synceventset_t remaining_ready_events = (synceventset_t)-1; // All clients are ready.

View File

@@ -117,9 +117,16 @@ class LL_COMMON_API AISyncKey
public:
// Constructor.
AISyncKey(void) : mStartFrameCount(LLFrameTimer::getFrameCount())
AISyncKey(AISyncKey const* from_key) : mStartFrameCount(from_key ? from_key->mStartFrameCount : LLFrameTimer::getFrameCount())
{
mFrameTimer.reset(sExpirationTime);
if (from_key)
{
mFrameTimer.copy(from_key->mFrameTimer);
}
else
{
mFrameTimer.reset(sExpirationTime);
}
}
// Destructor.
@@ -186,7 +193,7 @@ class LL_COMMON_API AISyncServer
// Add a new client to this server.
void add(AISyncClient* client);
// Add a new client to this server.
// Remove a client from this server.
void remove(AISyncClient* client);
// Return the key associated to this server (which is the key produced by the first client of the largest synckeytype_t that was added).
@@ -198,20 +205,19 @@ class LL_COMMON_API AISyncServer
bool never_synced(void) const { return !mSynchronized; }
// Set readiness of all events at once.
bool ready(synceventset_t events, synceventset_t yesno, AISyncClient* client);
void ready(synceventset_t events, synceventset_t yesno, AISyncClient* client);
// Unregister the (only) client because it's own its own and will never need synchronization.
void unregister_last_client(void);
// Return the events that all clients for.
synceventset_t events_with_all_clients_ready(void) const;
synceventset_t events_with_all_clients_ready(void) const { return mReadyEvents; }
// Return events that at least one client is ready for.
synceventset_t events_with_at_least_one_client_ready(void) const;
synceventset_t events_with_at_least_one_client_ready(void) const { return mPendingEvents; }
#ifdef SHOW_ASSERT
// Return a list of all registered clients.
client_list_t const& getClients(void) const { return mClients; }
#endif
private:
// Call event1_ready() or event1_not_ready() on all clients if the least significant bit of mReadyEvents changed.
@@ -238,7 +244,7 @@ class LL_COMMON_API AISyncServerMap : public LLSingleton<AISyncServerMap>
public:
// Find or create a server object that the client belongs to and store the client in it.
// If a new server is created, it is stored in mServers.
void register_client(AISyncClient* client);
void register_client(AISyncClient* client, AISyncKey* new_key);
private:
friend void intrusive_ptr_release(AISyncServer* server);
@@ -258,8 +264,8 @@ class LL_COMMON_API AISyncClient
synceventset_t mReadyEvents;
AISyncClient(void) : mReadyEvents(0) { }
#endif
virtual ~AISyncClient() { }
virtual AISyncKey* createSyncKey(void) const = 0;
virtual ~AISyncClient() { llassert(!mServer); /* If this fails then you need to add unregister_client() to the top of the destructor of the derived class that implements deregistered(). */ }
virtual AISyncKey* createSyncKey(AISyncKey const* from_key = NULL) const = 0;
virtual void event1_ready(void) = 0;
virtual void event1_not_ready(void) = 0;
@@ -275,21 +281,22 @@ class LL_COMMON_API AISyncClient
AISyncServer* server(void) const { return mServer.get(); }
// Add this client to a server with matching sync key. Optionally the server is first created.
void register_client(void) { AISyncServerMap::instance().register_client(this); }
void register_client(void) { AISyncServerMap::instance().register_client(this, createSyncKey()); }
// Remove this client from its server.
void unregister_client(void) { mServer->remove(this); }
// Remove this client from its server, if any.
void unregister_client(void) { if (mServer) mServer->remove(this); }
// Call 'ready' when you are ready (or not) to get a call to start().
// Returns true if that call was made (immediately), otherwise it may happen later.
bool ready(synceventset_t events, synceventset_t yesno) // Set readiness of all events at once.
// Set readiness of all events at once.
void ready(synceventset_t events, synceventset_t yesno)
{
if (!mServer)
{
register_client();
}
return mServer->ready(events, yesno, this);
mServer->ready(events, yesno, this);
}
};

View File

@@ -47,6 +47,10 @@ public:
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
//<singu>
void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; }
//</singu>
// Atomic reads of static variables.
// Return the number of seconds since the start of the application.
@@ -142,6 +146,9 @@ public:
bool hasExpired() const { return getElapsedSeconds() >= mExpiry; }
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); }
bool getStarted() const { return mRunning; }
//<singu>
F64 getStartTime() const { llassert(!mPaused); return mStartTime; }
//</singu>
// return the seconds since epoch when this timer will expire.
F64 expiresAt() const;

View File

@@ -5772,7 +5772,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
}
}
if (num_blocks)
//if (num_blocks) Singu note: commented out; having blocks or not is totally irrelevant!
{
avatarp->processAnimationStateChanges();
}

View File

@@ -231,7 +231,9 @@ void LLVOAvatar::startMotion(U32 bit, F32 time_offset)
{
if (!isMotionActive(bit))
{
mMotionController.disable_syncing(); // Don't attempt to synchronize AIMaskedMotion.
startMotion(mask2ID(bit), time_offset);
mMotionController.enable_syncing();
}
}
@@ -3810,6 +3812,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
if (LLVOAvatar::sShowAnimationDebug)
{
addDebugText(llformat("at=%.1f", mMotionController.getAnimTime()));
for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin();
iter != mMotionController.getActiveMotions().end(); ++iter)
{
@@ -3829,6 +3832,10 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
motionp->getName().c_str(),
(U32)motionp->getPriority());
}
if (motionp->server())
{
output += llformat(" rt=%.1f r=%d s=0x%xl", motionp->getRuntime(), motionp->mReadyEvents, motionp->server());
}
addDebugText(output);
}
}
@@ -5499,6 +5506,7 @@ void LLVOAvatar::processAnimationStateChanges()
}
// clear all current animations
BOOL const AOEnabled = gSavedSettings.getBOOL("AOEnabled"); // Singu note: put this outside the loop.
AnimIterator anim_it;
for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();)
{
@@ -5508,9 +5516,9 @@ void LLVOAvatar::processAnimationStateChanges()
if (found_anim == mSignaledAnimations.end())
{
if (isSelf())
if (AOEnabled && isSelf())
{
if ((gSavedSettings.getBOOL("AOEnabled")) && LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside
if (LLFloaterAO::stopMotion(anim_it->first, FALSE)) // if the AO replaced this anim serverside then stop it serverside
{
// return TRUE; //no local stop needed
}
@@ -5540,7 +5548,7 @@ void LLVOAvatar::processAnimationStateChanges()
// </edit>
if (processSingleAnimationStateChange(anim_it->first, TRUE))
{
if (isSelf() && gSavedSettings.getBOOL("AOEnabled")) // AO is only for ME
if (AOEnabled && isSelf()) // AO is only for ME
{
LLFloaterAO::startMotion(anim_it->first, 0,FALSE); // AO overrides the anim if needed
}