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");