[Orxonox-commit 836] r3352 - branches/resource/src/core
rgrieder at orxonox.net
rgrieder at orxonox.net
Sat Jul 25 22:16:38 CEST 2009
Author: rgrieder
Date: 2009-07-25 22:16:37 +0200 (Sat, 25 Jul 2009)
New Revision: 3352
Modified:
branches/resource/src/core/Game.cc
branches/resource/src/core/Game.h
Log:
Exported larger parts of the main into separate functions to have a clearer view. Also fixed a bug and improved fps limiter for Windows.
Modified: branches/resource/src/core/Game.cc
===================================================================
--- branches/resource/src/core/Game.cc 2009-07-25 20:11:12 UTC (rev 3351)
+++ branches/resource/src/core/Game.cc 2009-07-25 20:16:37 UTC (rev 3352)
@@ -121,17 +121,15 @@
this->bAbort_ = false;
bChangingState_ = false;
+#ifdef ORXONOX_PLATFORM_WINDOWS
+ minimumSleepTime_ = 1000/*us*/;
+#else
+ minimumSleepTime_ = 0/*us*/;
+#endif
+
// Create an empty root state
- declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
+ this->declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
- // reset statistics
- this->statisticsStartTime_ = 0;
- this->statisticsTickTimes_.clear();
- this->periodTickTime_ = 0;
- this->periodTime_ = 0;
- this->avgFPS_ = 0.0f;
- this->avgTickTime_ = 0.0f;
-
// Set up a basic clock to keep time
this->gameClock_ = new Clock();
@@ -194,6 +192,15 @@
if (this->requestedStateNodes_.empty())
COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
+ // reset statistics
+ this->statisticsStartTime_ = 0;
+ this->statisticsTickTimes_.clear();
+ this->periodTickTime_ = 0;
+ this->periodTime_ = 0;
+ this->avgFPS_ = 0.0f;
+ this->avgTickTime_ = 0.0f;
+ this->excessSleepTime_ = 0;
+
// START GAME
// first delta time should be about 0 seconds
this->gameClock_->capture();
@@ -202,127 +209,159 @@
statisticsTickTimes_.push_back(tickInfo);
while (!this->bAbort_ && (!this->activeStates_.empty() || this->requestedStateNodes_.size() > 0))
{
- uint64_t currentTime = this->gameClock_->getRealMicroseconds();
-
- uint64_t nextTickTime = statisticsTickTimes_.back().tickTime + static_cast<uint64_t>(1000000.0f / configuration_->fpsLimit_);
- if (currentTime < nextTickTime)
- {
- usleep(nextTickTime - currentTime);
- continue;
- }
+ // Generate the dt
this->gameClock_->capture();
- // STATISTICS
- StatisticsTickInfo tickInfo = {currentTime, 0};
+ // Statistics init
+ StatisticsTickInfo tickInfo = {gameClock_->getMicroseconds(), 0};
statisticsTickTimes_.push_back(tickInfo);
this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
- // UPDATE STATE STACK
- while (this->requestedStateNodes_.size() > 0)
+ // Update the GameState stack if required
+ this->updateGameStateStack();
+
+ // Core preUpdate (doesn't throw)
+ if (!this->core_->preUpdate(*this->gameClock_))
{
- shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
- assert(this->activeStateNode_);
- if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
- this->unloadState(this->activeStateNode_->state_);
- else // has to be child
- {
- try
- {
- this->loadState(requestedStateNode->state_);
- }
- catch (const std::exception& ex)
- {
- COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
- // All scheduled operations have now been rendered inert --> flush them and issue a warning
- if (this->requestedStateNodes_.size() > 1)
- COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
- this->requestedStateNodes_.clear();
- break;
- }
- }
- this->activeStateNode_ = requestedStateNode;
- this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
+ this->stop();
+ break;
}
- // UPDATE, Core preUpdate (doesn't throw)
- if (!this->core_->preUpdate(*this->gameClock_))
+ // Update the GameStates bottom up in the stack
+ this->updateGameStates();
+
+ // Core postUpdate (doesn't throw)
+ if (!this->core_->postUpdate(*this->gameClock_))
{
this->stop();
break;
}
- // UPDATE, GameStates bottom to top in the stack
- // Note: The first element is the empty root state, which doesn't need ticking
- for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
- it != this->activeStates_.end(); ++it)
+ // Evaluate statistics
+ this->updateStatistics();
+
+ // Limit framerate
+ this->updateFPSLimiter();
+ }
+
+ // UNLOAD all remaining states
+ while (this->activeStates_.size() > 1)
+ this->unloadState(this->activeStates_.back());
+ this->activeStateNode_ = this->rootStateNode_;
+ this->requestedStateNodes_.clear();
+ }
+
+ void Game::updateGameStateStack()
+ {
+ while (this->requestedStateNodes_.size() > 0)
+ {
+ shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
+ assert(this->activeStateNode_);
+ if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
+ this->unloadState(this->activeStateNode_->state_);
+ else // has to be child
{
- std::string exceptionMessage;
try
{
- // Add tick time for most of the states
- uint64_t timeBeforeTick;
- if ((*it)->ignoreTickTime())
- timeBeforeTick = this->gameClock_->getRealMicroseconds();
- (*it)->update(*this->gameClock_);
- if ((*it)->ignoreTickTime())
- this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
+ this->loadState(requestedStateNode->state_);
}
catch (const std::exception& ex)
- { exceptionMessage = ex.what(); }
- catch (...)
- { exceptionMessage = "Unknown exception"; }
- if (!exceptionMessage.empty())
{
- COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
- COUT(1) << "This should really never happen!" << std::endl;
- COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
- if ((*it)->getParent() != NULL)
- this->requestState((*it)->getParent()->getName());
- else
- this->stop();
+ COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
+ // All scheduled operations have now been rendered inert --> flush them and issue a warning
+ if (this->requestedStateNodes_.size() > 1)
+ COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
+ this->requestedStateNodes_.clear();
break;
}
-
}
+ this->activeStateNode_ = requestedStateNode;
+ this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
+ }
+ }
- // UPDATE, Core postUpdate (doesn't throw)
- if (!this->core_->postUpdate(*this->gameClock_))
+ void Game::updateGameStates()
+ {
+ // Note: The first element is the empty root state, which doesn't need ticking
+ for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
+ it != this->activeStates_.end(); ++it)
+ {
+ std::string exceptionMessage;
+ try
{
- this->stop();
+ // Add tick time for most of the states
+ uint64_t timeBeforeTick;
+ if ((*it)->ignoreTickTime())
+ timeBeforeTick = this->gameClock_->getRealMicroseconds();
+ (*it)->update(*this->gameClock_);
+ if ((*it)->ignoreTickTime())
+ this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
+ }
+ catch (const std::exception& ex)
+ { exceptionMessage = ex.what(); }
+ catch (...)
+ { exceptionMessage = "Unknown exception"; }
+ if (!exceptionMessage.empty())
+ {
+ COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
+ COUT(1) << "This should really never happen!" << std::endl;
+ COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
+ if ((*it)->getParent() != NULL)
+ this->requestState((*it)->getParent()->getName());
+ else
+ this->stop();
break;
}
+ }
+ }
- // STATISTICS
- if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
+ void Game::updateStatistics()
+ {
+ // Add the tick time of this frame (rendering time has already been subtracted)
+ uint64_t currentTime = gameClock_->getMicroseconds();
+ uint64_t currentRealTime = gameClock_->getRealMicroseconds();
+ this->statisticsTickTimes_.back().tickLength += currentRealTime - currentTime;
+ this->periodTickTime_ += currentRealTime - currentTime;
+ if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
+ {
+ std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
+ assert(it != this->statisticsTickTimes_.end());
+ int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
+ if (static_cast<int64_t>(it->tickTime) < lastTime)
{
- std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
- assert(it != this->statisticsTickTimes_.end());
- int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
- if (static_cast<int64_t>(it->tickTime) < lastTime)
+ do
{
- do
- {
- assert(this->periodTickTime_ >= it->tickLength);
- this->periodTickTime_ -= it->tickLength;
- ++it;
- assert(it != this->statisticsTickTimes_.end());
- } while (static_cast<int64_t>(it->tickTime) < lastTime);
- this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
- }
+ assert(this->periodTickTime_ >= it->tickLength);
+ this->periodTickTime_ -= it->tickLength;
+ ++it;
+ assert(it != this->statisticsTickTimes_.end());
+ } while (static_cast<int64_t>(it->tickTime) < lastTime);
+ this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
+ }
- uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
- this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
- this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
+ uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
+ this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
+ this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
- this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
- }
+ this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
}
+ }
- // UNLOAD all remaining states
- while (this->activeStates_.size() > 1)
- this->unloadState(this->activeStates_.back());
- this->activeStateNode_ = this->rootStateNode_;
- this->requestedStateNodes_.clear();
+ void Game::updateFPSLimiter()
+ {
+ // Why configuration_->fpsLimit_ - 1? No idea, but otherwise the fps rate is always (from 10 to 200!) one frame too high
+ uint32_t nextTime = gameClock_->getMicroseconds() - excessSleepTime_ + static_cast<uint32_t>(1000000.0f / (configuration_->fpsLimit_ - 1));
+ uint64_t currentRealTime = gameClock_->getRealMicroseconds();
+ while (currentRealTime < nextTime - minimumSleepTime_)
+ {
+ usleep(nextTime - currentRealTime);
+ currentRealTime = gameClock_->getRealMicroseconds();
+ }
+ // Integrate excess to avoid steady state error
+ excessSleepTime_ = currentRealTime - nextTime;
+ // Anti windup
+ if (excessSleepTime_ > 50000) // 20ms is about the maximum time Windows would sleep for too long
+ excessSleepTime_ = 50000;
}
void Game::stop()
Modified: branches/resource/src/core/Game.h
===================================================================
--- branches/resource/src/core/Game.h 2009-07-25 20:11:12 UTC (rev 3351)
+++ branches/resource/src/core/Game.h 2009-07-25 20:16:37 UTC (rev 3352)
@@ -132,6 +132,12 @@
void loadState(GameState* state);
void unloadState(GameState* state);
+ // Main loop structuring
+ void updateGameStateStack();
+ void updateGameStates();
+ void updateStatistics();
+ void updateFPSLimiter();
+
std::map<std::string, GameState*> gameStates_;
std::vector<GameState*> activeStates_;
boost::shared_ptr<GameStateTreeNode> rootStateNode_;
@@ -152,6 +158,8 @@
uint32_t periodTickTime_;
float avgFPS_;
float avgTickTime_;
+ int excessSleepTime_;
+ unsigned int minimumSleepTime_;
static std::map<std::string, GameStateInfo> gameStateDeclarations_s;
static Game* singletonRef_s; //!< Pointer to the Singleton
More information about the Orxonox-commit
mailing list