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

@@ -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;