diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index f08ab8892..d0576212c 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -15,6 +15,7 @@ include_directories(
)
set(llcommon_SOURCE_FILES
+ aiframetimer.cpp
aiaprpool.cpp
imageids.cpp
indra_constants.cpp
@@ -84,6 +85,7 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
+ aiframetimer.h
aiaprpool.h
aithreadsafe.h
bitpack.h
diff --git a/indra/llcommon/aiframetimer.cpp b/indra/llcommon/aiframetimer.cpp
new file mode 100644
index 000000000..66da2f02c
--- /dev/null
+++ b/indra/llcommon/aiframetimer.cpp
@@ -0,0 +1,39 @@
+/**
+ * @file aiframetimer.cpp
+ *
+ * Copyright (c) 2011, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 06/08/2011
+ * - Initial version, written by Aleric Inglewood @ SL
+ */
+
+#include "linden_common.h"
+
+#include "aiframetimer.h"
+
+F64 AIFrameTimer::sNextExpiration;
+
+void AIFrameTimer::handleExpiration(F64 current_frame_time)
+{
+}
+
diff --git a/indra/llcommon/aiframetimer.h b/indra/llcommon/aiframetimer.h
new file mode 100644
index 000000000..c3e380d69
--- /dev/null
+++ b/indra/llcommon/aiframetimer.h
@@ -0,0 +1,54 @@
+/**
+ * @file aiframetimer.h
+ * @brief Implementation of AIFrameTimer.
+ *
+ * Copyright (c) 2011, Aleric Inglewood.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution.
+ *
+ * CHANGELOG
+ * and additional copyright holders.
+ *
+ * 05/08/2011
+ * Initial version, written by Aleric Inglewood @ SL
+ */
+
+#ifndef AIFRAMETIMER_H
+#define AIFRAMETIMER_H
+
+#include "llframetimer.h"
+
+class LL_COMMON_API AIFrameTimer
+{
+ private:
+ F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
+ static std::set sTimerList; // List with all running timers.
+
+ friend class LLFrameTimer;
+ static F64 sNextExpiration; // Cache of smallest value in sTimerList.
+
+ public:
+ AIFrameTimer(F64 expiration) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration) { }
+
+ static void handleExpiration(F64 current_frame_time);
+
+ friend bool operator<(AIFrameTimer const& ft1, AIFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
+};
+
+
+#endif
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
index d05b9875c..73059eca3 100644
--- a/indra/llcommon/llframetimer.cpp
+++ b/indra/llcommon/llframetimer.cpp
@@ -35,6 +35,8 @@
#include "u64.h"
#include "llframetimer.h"
+#include "aiframetimer.h"
+#include "aiaprpool.h"
// Local constants.
static F64 const USEC_PER_SECOND = 1000000.0;
@@ -44,47 +46,46 @@ static F64 const USEC_TO_SEC_F64 = 0.000001;
U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch.
U64 LLFrameTimer::sTotalTime = LLFrameTimer::sStartTotalTime; // Current time in microseconds since epoch, updated at least once per frame.
F64 LLFrameTimer::sTotalSeconds = // Current time in seconds since epoch, updated together with LLFrameTimer::sTotalTime.
- U64_to_F64(LLFrameTimer::sTotalTime) * USEC_TO_SEC_F64;
+ U64_to_F64(LLFrameTimer::sTotalTime) * USEC_TO_SEC_F64;
F64 LLFrameTimer::sFrameTime = 0.0; // Current time in seconds since application start, updated together with LLFrameTimer::sTotalTime.
// Updated exactly once per frame:
S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start).
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
+// Mutex for the above.
+apr_thread_mutex_t* LLFrameTimer::sGlobalMutex;
// static
-void LLFrameTimer::updateFrameTime()
+void LLFrameTimer::global_initialization(void)
{
+ apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, AIAPRRootPool::get()());
+}
+
+// static
+void LLFrameTimer::updateFrameTime(void)
+{
+ llassert(is_main_thread());
sTotalTime = totalTime();
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
- sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
-}
+ F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
+ apr_thread_mutex_lock(sGlobalMutex);
+ sFrameTime = new_frame_time;
+ apr_thread_mutex_unlock(sGlobalMutex);
+}
// static
-void LLFrameTimer::updateFrameTimeAndCount()
+void LLFrameTimer::updateFrameTimeAndCount(void)
{
updateFrameTime();
sFrameDeltaTime = sTotalTime - sPrevTotalTime;
sPrevTotalTime = sTotalTime;
++sFrameCount;
-}
-void LLFrameTimer::reset(F32 expiration)
-{
- llassert(!mPaused);
- mStartTime = sFrameTime;
- mExpiry = sFrameTime + expiration;
-}
-
-void LLFrameTimer::start(F32 expiration)
-{
- reset(expiration);
- mRunning = true; // Start, if not already started.
-}
-
-void LLFrameTimer::stop()
-{
- llassert(!mPaused);
- mRunning = false;
+ // Handle AIFrameTimer expiration and callbacks.
+ if (AIFrameTimer::sNextExpiration <= sFrameTime)
+ {
+ AIFrameTimer::handleExpiration(sFrameTime);
+ }
}
// Don't combine pause/unpause with start/stop
@@ -95,38 +96,38 @@ void LLFrameTimer::stop()
// foo.unpause() // unpauses
// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
// Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
-void LLFrameTimer::pause()
+void LLFrameTimer::pause(void)
{
+ llassert(is_main_thread());
if (!mPaused)
{
+ // Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far.
}
mPaused = true;
}
-void LLFrameTimer::unpause()
+void LLFrameTimer::unpause(void)
{
+ llassert(is_main_thread());
if (mPaused)
{
+ // Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far.
}
mPaused = false;
}
-void LLFrameTimer::setTimerExpirySec(F32 expiration)
-{
- llassert(!mPaused);
- mExpiry = mStartTime + expiration;
-}
-
void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
{
+ llassert(is_main_thread());
llassert(!mPaused);
+ // Only the main thread writes to sFrameTime, so there is no need for locking.
mStartTime = sFrameTime;
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
}
-F64 LLFrameTimer::expiresAt() const
+F64 LLFrameTimer::expiresAt(void) const
{
F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
expires_at += mExpiry;
@@ -135,31 +136,47 @@ F64 LLFrameTimer::expiresAt() const
bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
{
- if (hasExpired())
+ llassert(!mPaused);
+ F64 frame_time = getElapsedSeconds();
+ if (frame_time >= mExpiry)
{
- reset(expiration);
+ mStartTime = frame_time;
+ mExpiry = mStartTime + expiration;
return true;
}
return false;
}
-// static
-F32 LLFrameTimer::getFrameDeltaTimeF32()
+F32 LLFrameTimer::getElapsedTimeAndResetF32(void)
{
+ llassert(mRunning && !mPaused);
+ F64 frame_time = getElapsedSeconds();
+ F32 elapsed_time = (F32)(frame_time - mStartTime);
+ mExpiry = mStartTime = frame_time;
+ return elapsed_time;
+}
+
+// static
+// Return number of seconds between the last two frames.
+F32 LLFrameTimer::getFrameDeltaTimeF32(void)
+{
+ llassert(is_main_thread());
+ // Only the main thread writes to sFrameDeltaTime, so there is no need for locking.
return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
}
-
-// static
+// static
// Return seconds since the current frame started
-F32 LLFrameTimer::getCurrentFrameTime()
+F32 LLFrameTimer::getCurrentFrameTime(void)
{
+ llassert(is_main_thread());
+ // Only the main thread writes to sTotalTime, so there is no need for locking.
U64 frame_time = totalTime() - sTotalTime;
return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
}
// Glue code to avoid full class .h file #includes
-F32 getCurrentFrameTime()
+F32 getCurrentFrameTime(void)
{
return (F32)(LLFrameTimer::getCurrentFrameTime());
}
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
index e737a884e..bae3e5615 100644
--- a/indra/llcommon/llframetimer.h
+++ b/indra/llcommon/llframetimer.h
@@ -43,88 +43,131 @@
#include "lltimer.h"
#include "timing.h"
+#include
class LL_COMMON_API LLFrameTimer
{
public:
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
- LLFrameTimer(void) : mStartTime(sFrameTime), mExpiry(0), mRunning(true), mPaused(false) { }
+ LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
+
+ // Atomic reads of static variables.
// Return the number of seconds since the start of the application.
- static F64 getElapsedSeconds()
+ static F64 getElapsedSeconds(void)
{
// Loses msec precision after ~4.5 hours...
- return sFrameTime;
+ apr_thread_mutex_lock(sGlobalMutex);
+ F64 res = sFrameTime;
+ apr_thread_mutex_unlock(sGlobalMutex);
+ return res;
}
// Return a low precision usec since epoch.
- static U64 getTotalTime()
+ static U64 getTotalTime(void)
{
- llassert(sTotalTime);
- return sTotalTime;
+ // sTotalTime is only accessed by the main thread, so no locking is necessary.
+ llassert(is_main_thread());
+ //apr_thread_mutex_lock(sGlobalMutex);
+ U64 res = sTotalTime;
+ //apr_thread_mutex_unlock(sGlobalMutex);
+ llassert(res);
+ return res;
}
// Return a low precision seconds since epoch.
- static F64 getTotalSeconds()
+ static F64 getTotalSeconds(void)
{
- return sTotalSeconds;
+ // sTotalSeconds is only accessed by the main thread, so no locking is necessary.
+ llassert(is_main_thread());
+ //apr_thread_mutex_lock(sGlobalMutex);
+ F64 res = sTotalSeconds;
+ //apr_thread_mutex_unlock(sGlobalMutex);
+ return res;
+ }
+
+ // Return current frame number (the number of frames since application start).
+ static U32 getFrameCount(void)
+ {
+ // sFrameCount is only accessed by the main thread, so no locking is necessary.
+ llassert(is_main_thread());
+ //apr_thread_mutex_lock(sGlobalMutex);
+ U32 res = sFrameCount;
+ //apr_thread_mutex_unlock(sGlobalMutex);
+ return res;
}
// Call this method once per frame to update the current frame time.
// This is actually called at some other times as well.
- static void updateFrameTime();
+ static void updateFrameTime(void);
// Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime.
- static void updateFrameTimeAndCount();
-
- // Return current frame number (the number of frames since application start).
- static U32 getFrameCount() { return sFrameCount; }
+ static void updateFrameTimeAndCount(void);
// Return duration of last frame in seconds.
- static F32 getFrameDeltaTimeF32();
+ static F32 getFrameDeltaTimeF32(void);
// Return seconds since the current frame started
- static F32 getCurrentFrameTime();
+ static F32 getCurrentFrameTime(void);
// MANIPULATORS
- void reset(F32 expiration = 0.f); // Same as start() but leaves mRunning off when called after stop().
- void start(F32 expiration = 0.f); // Reset and (re)start with expiration.
- void stop(); // Stop running.
+ void reset(F32 expiration = 0.f) // Same as start() but leaves mRunning off when called after stop().
+ {
+ llassert(!mPaused);
+ mStartTime = getElapsedSeconds();
+ mExpiry = mStartTime + expiration;
+ }
+
+ void start(F32 expiration = 0.f) // Reset and (re)start with expiration.
+ {
+ reset(expiration);
+ mRunning = true; // Start, if not already started.
+ }
+
+ void stop(void) // Stop running.
+ {
+ llassert(!mPaused);
+ mRunning = false;
+ }
void pause(); // Mark elapsed time so far.
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
- void setTimerExpirySec(F32 expiration);
+ void setTimerExpirySec(F32 expiration) { llassert(!mPaused); mExpiry = mStartTime + expiration; }
+
void setExpiryAt(F64 seconds_since_epoch);
bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired.
- F32 getElapsedTimeAndResetF32() { F32 t = getElapsedTimeF32(); reset(); return t; }
- void setAge(const F64 age) { llassert(!mPaused); mStartTime = sFrameTime - age; }
+ F32 getElapsedTimeAndResetF32(void);
+ void setAge(const F64 age) { llassert(!mPaused); mStartTime = getElapsedSeconds() - age; }
// ACCESSORS
- bool hasExpired() const { return sFrameTime >= mExpiry; }
- F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(sFrameTime - mStartTime); }
+ bool hasExpired() const { return getElapsedSeconds() >= mExpiry; }
+ F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); }
bool getStarted() const { return mRunning; }
// return the seconds since epoch when this timer will expire.
F64 expiresAt() const;
-protected:
- // A single, high resolution timer that drives all LLFrameTimers
- // *NOTE: no longer used.
- //static LLTimer sInternalTimer;
+public:
+ // Do one-time initialization of the static members.
+ static void global_initialization(void);
+protected:
//
- // Aplication constants
+ // Application constants
//
// Application start in microseconds since epoch.
static U64 const sStartTotalTime;
//
- // Data updated per frame
+ // Global data.
//
+ // More than one thread are accessing (some of) these variables, therefore we need locking.
+ static apr_thread_mutex_t* sGlobalMutex;
+
// Current time in seconds since application start, updated together with sTotalTime.
static F64 sFrameTime;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 3b1436474..50feb455a 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -560,6 +560,9 @@ bool LLAppViewer::init()
// we run the "program crashed last time" error handler below.
//
+ // We can call this early.
+ LLFrameTimer::global_initialization();
+
// Need to do this initialization before we do anything else, since anything
// that touches files should really go through the lldir API
gDirUtilp->initAppDirs("SecondLife");