Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Jul 25, 2009, 10:16:37 PM (15 years ago)
Author:
rgrieder
Message:

Exported larger parts of the main into separate functions to have a clearer view. Also fixed a bug and improved fps limiter for Windows.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • code/branches/resource/src/core/Game.cc

    r3349 r3352  
    122122        bChangingState_ = false;
    123123
     124#ifdef ORXONOX_PLATFORM_WINDOWS
     125        minimumSleepTime_ = 1000/*us*/;
     126#else
     127        minimumSleepTime_ = 0/*us*/;
     128#endif
     129
    124130        // Create an empty root state
    125         declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
    126 
    127         // reset statistics
    128         this->statisticsStartTime_ = 0;
    129         this->statisticsTickTimes_.clear();
    130         this->periodTickTime_ = 0;
    131         this->periodTime_ = 0;
    132         this->avgFPS_ = 0.0f;
    133         this->avgTickTime_ = 0.0f;
     131        this->declareGameState<GameState>("GameState", "emptyRootGameState", true, false);
    134132
    135133        // Set up a basic clock to keep time
     
    195193            COUT(0) << "Warning: Starting game without requesting GameState. This automatically terminates the program." << std::endl;
    196194
     195        // reset statistics
     196        this->statisticsStartTime_ = 0;
     197        this->statisticsTickTimes_.clear();
     198        this->periodTickTime_ = 0;
     199        this->periodTime_ = 0;
     200        this->avgFPS_ = 0.0f;
     201        this->avgTickTime_ = 0.0f;
     202        this->excessSleepTime_ = 0;
     203
    197204        // START GAME
    198205        // first delta time should be about 0 seconds
     
    203210        while (!this->bAbort_ && (!this->activeStates_.empty() || this->requestedStateNodes_.size() > 0))
    204211        {
    205             uint64_t currentTime = this->gameClock_->getRealMicroseconds();
    206 
    207             uint64_t nextTickTime = statisticsTickTimes_.back().tickTime + static_cast<uint64_t>(1000000.0f / configuration_->fpsLimit_);
    208             if (currentTime < nextTickTime)
    209             {
    210                 usleep(nextTickTime - currentTime);
    211                 continue;
    212             }
     212            // Generate the dt
    213213            this->gameClock_->capture();
    214214
    215             // STATISTICS
    216             StatisticsTickInfo tickInfo = {currentTime, 0};
     215            // Statistics init
     216            StatisticsTickInfo tickInfo = {gameClock_->getMicroseconds(), 0};
    217217            statisticsTickTimes_.push_back(tickInfo);
    218218            this->periodTime_ += this->gameClock_->getDeltaTimeMicroseconds();
    219219
    220             // UPDATE STATE STACK
    221             while (this->requestedStateNodes_.size() > 0)
    222             {
    223                 shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
    224                 assert(this->activeStateNode_);
    225                 if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
    226                     this->unloadState(this->activeStateNode_->state_);
    227                 else // has to be child
    228                 {
    229                     try
    230                     {
    231                         this->loadState(requestedStateNode->state_);
    232                     }
    233                     catch (const std::exception& ex)
    234                     {
    235                         COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
    236                         // All scheduled operations have now been rendered inert --> flush them and issue a warning
    237                         if (this->requestedStateNodes_.size() > 1)
    238                             COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
    239                         this->requestedStateNodes_.clear();
    240                         break;
    241                     }
    242                 }
    243                 this->activeStateNode_ = requestedStateNode;
    244                 this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
    245             }
    246 
    247             // UPDATE, Core preUpdate (doesn't throw)
     220            // Update the GameState stack if required
     221            this->updateGameStateStack();
     222
     223            // Core preUpdate (doesn't throw)
    248224            if (!this->core_->preUpdate(*this->gameClock_))
    249225            {
     
    252228            }
    253229
    254             // UPDATE, GameStates bottom to top in the stack
    255             // Note: The first element is the empty root state, which doesn't need ticking
    256             for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
    257                 it != this->activeStates_.end(); ++it)
    258             {
    259                 std::string exceptionMessage;
    260                 try
    261                 {
    262                     // Add tick time for most of the states
    263                     uint64_t timeBeforeTick;
    264                     if ((*it)->ignoreTickTime())
    265                         timeBeforeTick = this->gameClock_->getRealMicroseconds();
    266                     (*it)->update(*this->gameClock_);
    267                     if ((*it)->ignoreTickTime())
    268                         this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
    269                 }
    270                 catch (const std::exception& ex)
    271                 { exceptionMessage = ex.what(); }
    272                 catch (...)
    273                 { exceptionMessage = "Unknown exception"; }
    274                 if (!exceptionMessage.empty())
    275                 {
    276                     COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
    277                     COUT(1) << "This should really never happen!" << std::endl;
    278                     COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
    279                     if ((*it)->getParent() != NULL)
    280                         this->requestState((*it)->getParent()->getName());
    281                     else
    282                         this->stop();
    283                     break;
    284                 }
    285 
    286             }
    287 
    288             // UPDATE, Core postUpdate (doesn't throw)
     230            // Update the GameStates bottom up in the stack
     231            this->updateGameStates();
     232
     233            // Core postUpdate (doesn't throw)
    289234            if (!this->core_->postUpdate(*this->gameClock_))
    290235            {
     
    293238            }
    294239
    295             // STATISTICS
    296             if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
    297             {
    298                 std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
    299                 assert(it != this->statisticsTickTimes_.end());
    300                 int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
    301                 if (static_cast<int64_t>(it->tickTime) < lastTime)
    302                 {
    303                     do
    304                     {
    305                         assert(this->periodTickTime_ >= it->tickLength);
    306                         this->periodTickTime_ -= it->tickLength;
    307                         ++it;
    308                         assert(it != this->statisticsTickTimes_.end());
    309                     } while (static_cast<int64_t>(it->tickTime) < lastTime);
    310                     this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
    311                 }
    312 
    313                 uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
    314                 this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
    315                 this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
    316 
    317                 this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
    318             }
     240            // Evaluate statistics
     241            this->updateStatistics();
     242
     243            // Limit framerate
     244            this->updateFPSLimiter();
    319245        }
    320246
     
    324250        this->activeStateNode_ = this->rootStateNode_;
    325251        this->requestedStateNodes_.clear();
     252    }
     253
     254    void Game::updateGameStateStack()
     255    {
     256        while (this->requestedStateNodes_.size() > 0)
     257        {
     258            shared_ptr<GameStateTreeNode> requestedStateNode = this->requestedStateNodes_.front();
     259            assert(this->activeStateNode_);
     260            if (!this->activeStateNode_->parent_.expired() && requestedStateNode == this->activeStateNode_->parent_.lock())
     261                this->unloadState(this->activeStateNode_->state_);
     262            else // has to be child
     263            {
     264                try
     265                {
     266                    this->loadState(requestedStateNode->state_);
     267                }
     268                catch (const std::exception& ex)
     269                {
     270                    COUT(1) << "Error: Loading GameState '" << requestedStateNode->state_->getName() << "' failed: " << ex.what() << std::endl;
     271                    // All scheduled operations have now been rendered inert --> flush them and issue a warning
     272                    if (this->requestedStateNodes_.size() > 1)
     273                        COUT(1) << "All " << this->requestedStateNodes_.size() - 1 << " scheduled transitions have been ignored." << std::endl;
     274                    this->requestedStateNodes_.clear();
     275                    break;
     276                }
     277            }
     278            this->activeStateNode_ = requestedStateNode;
     279            this->requestedStateNodes_.erase(this->requestedStateNodes_.begin());
     280        }
     281    }
     282
     283    void Game::updateGameStates()
     284    {
     285        // Note: The first element is the empty root state, which doesn't need ticking
     286        for (std::vector<GameState*>::const_iterator it = this->activeStates_.begin() + 1;
     287            it != this->activeStates_.end(); ++it)
     288        {
     289            std::string exceptionMessage;
     290            try
     291            {
     292                // Add tick time for most of the states
     293                uint64_t timeBeforeTick;
     294                if ((*it)->ignoreTickTime())
     295                    timeBeforeTick = this->gameClock_->getRealMicroseconds();
     296                (*it)->update(*this->gameClock_);
     297                if ((*it)->ignoreTickTime())
     298                    this->subtractTickTime(static_cast<int32_t>(this->gameClock_->getRealMicroseconds() - timeBeforeTick));
     299            }
     300            catch (const std::exception& ex)
     301            { exceptionMessage = ex.what(); }
     302            catch (...)
     303            { exceptionMessage = "Unknown exception"; }
     304            if (!exceptionMessage.empty())
     305            {
     306                COUT(1) << "An exception occurred while updating '" << (*it)->getName() << "': " << exceptionMessage << std::endl;
     307                COUT(1) << "This should really never happen!" << std::endl;
     308                COUT(1) << "Unloading all GameStates depending on the one that crashed." << std::endl;
     309                if ((*it)->getParent() != NULL)
     310                    this->requestState((*it)->getParent()->getName());
     311                else
     312                    this->stop();
     313                break;
     314            }
     315        }
     316    }
     317
     318    void Game::updateStatistics()
     319    {
     320        // Add the tick time of this frame (rendering time has already been subtracted)
     321        uint64_t currentTime = gameClock_->getMicroseconds();
     322        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
     323        this->statisticsTickTimes_.back().tickLength += currentRealTime - currentTime;
     324        this->periodTickTime_ += currentRealTime - currentTime;
     325        if (this->periodTime_ > this->configuration_->statisticsRefreshCycle_)
     326        {
     327            std::list<StatisticsTickInfo>::iterator it = this->statisticsTickTimes_.begin();
     328            assert(it != this->statisticsTickTimes_.end());
     329            int64_t lastTime = currentTime - this->configuration_->statisticsAvgLength_;
     330            if (static_cast<int64_t>(it->tickTime) < lastTime)
     331            {
     332                do
     333                {
     334                    assert(this->periodTickTime_ >= it->tickLength);
     335                    this->periodTickTime_ -= it->tickLength;
     336                    ++it;
     337                    assert(it != this->statisticsTickTimes_.end());
     338                } while (static_cast<int64_t>(it->tickTime) < lastTime);
     339                this->statisticsTickTimes_.erase(this->statisticsTickTimes_.begin(), it);
     340            }
     341
     342            uint32_t framesPerPeriod = this->statisticsTickTimes_.size();
     343            this->avgFPS_ = static_cast<float>(framesPerPeriod) / (currentTime - this->statisticsTickTimes_.front().tickTime) * 1000000.0f;
     344            this->avgTickTime_ = static_cast<float>(this->periodTickTime_) / framesPerPeriod / 1000.0f;
     345
     346            this->periodTime_ -= this->configuration_->statisticsRefreshCycle_;
     347        }
     348    }
     349
     350    void Game::updateFPSLimiter()
     351    {
     352        // Why configuration_->fpsLimit_ - 1? No idea, but otherwise the fps rate is always (from 10 to 200!) one frame too high
     353        uint32_t nextTime = gameClock_->getMicroseconds() - excessSleepTime_ + static_cast<uint32_t>(1000000.0f / (configuration_->fpsLimit_ - 1));
     354        uint64_t currentRealTime = gameClock_->getRealMicroseconds();
     355        while (currentRealTime < nextTime - minimumSleepTime_)
     356        {
     357            usleep(nextTime - currentRealTime);
     358            currentRealTime = gameClock_->getRealMicroseconds();
     359        }
     360        // Integrate excess to avoid steady state error
     361        excessSleepTime_ = currentRealTime - nextTime;
     362        // Anti windup
     363        if (excessSleepTime_ > 50000) // 20ms is about the maximum time Windows would sleep for too long
     364            excessSleepTime_ = 50000;
    326365    }
    327366
Note: See TracChangeset for help on using the changeset viewer.