- Cleanup and rewrite some mainloop code
authorMatthias Braun <matze@braunis.de>
Sun, 21 Jan 2007 16:49:24 +0000 (16:49 +0000)
committerMatthias Braun <matze@braunis.de>
Sun, 21 Jan 2007 16:49:24 +0000 (16:49 +0000)
- We don't do aritifical SDL_Delay anymore to save CPU cycles, activate
  vsyncing in your opengl driver if you want that.

SVN-Revision: 4627

src/mainloop.cpp
src/mainloop.hpp
src/worldmap/worldmap.cpp

index d0b6236..2001a14 100644 (file)
 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
 // binary fraction...
 static const float LOGICAL_FPS = 64.0;
+/** ticks (as returned from SDL_GetTicks) per frame */
+static const Uint32 TICKS_PER_FRAME = (Uint32) (1000.0 / LOGICAL_FPS);
+/** don't skip more than every 2nd frame */
+static const int MAX_FRAME_SKIP = 2;
 
 MainLoop* main_loop = NULL;
 
@@ -116,126 +120,143 @@ MainLoop::draw_fps(DrawingContext& context, float fps_fps)
 }
 
 void
+MainLoop::draw(DrawingContext& context)
+{
+  static Uint32 fps_ticks = SDL_GetTicks();
+  static int frame_count = 0;
+
+  current_screen->draw(context);
+  if(Menu::current() != NULL)
+    Menu::current()->draw(context);
+  if(screen_fade.get() != NULL)
+    screen_fade->draw(context);
+  Console::instance->draw(context);
+
+  if(config->show_fps)
+    draw_fps(context, fps);
+
+  context.do_drawing();
+
+  /* Calculate frames per second */
+  if(config->show_fps)
+  {
+    ++frame_count;
+
+    if(SDL_GetTicks() - fps_ticks >= 500)
+    {
+      fps = (float) frame_count / .5;
+      frame_count = 0;
+      fps_ticks = SDL_GetTicks();
+    }
+  }
+}
+
+void
+MainLoop::update_gamelogic(float elapsed_time)
+{
+  Scripting::update_debugger();
+  Scripting::TimeScheduler::instance->update(game_time);
+  current_screen->update(elapsed_time);
+  if(screen_fade.get() != NULL)
+    screen_fade->update(elapsed_time);
+  Console::instance->update(elapsed_time);
+}
+
+void
+MainLoop::process_events()
+{
+  main_controller->update();
+  SDL_Event event;
+  while(SDL_PollEvent(&event)) {
+    main_controller->process_event(event);
+    if(Menu::current() != NULL)
+      Menu::current()->event(event);
+    if(event.type == SDL_QUIT)
+      quit();
+  }
+}
+
+void
+MainLoop::handle_screen_switch()
+{
+  while( (next_screen.get() != NULL || nextpop) &&
+      (screen_fade.get() == NULL || screen_fade->done())) {
+    if(current_screen.get() != NULL) {
+      current_screen->leave();
+    }
+
+    if(nextpop) {
+      if(screen_stack.empty()) {
+        running = false;
+        break;
+      }
+      next_screen.reset(screen_stack.back());
+      screen_stack.pop_back();
+    }
+    if(nextpush && current_screen.get() != NULL) {
+      screen_stack.push_back(current_screen.release());
+    }
+
+    nextpush = false;
+    nextpop = false;
+    speed = 1.0;
+    if(next_screen.get() != NULL)
+      next_screen->setup();
+    current_screen.reset(next_screen.release());
+    screen_fade.reset(NULL);
+
+    waiting_threads.wakeup();
+  }
+}
+
+void
 MainLoop::run()
 {
   DrawingContext context;
 
-  unsigned int frame_count = 0;
-  float fps_fps = 0;
-  Uint32 fps_ticks = SDL_GetTicks();
-  Uint32 fps_nextframe_ticks = SDL_GetTicks();
-  Uint32 ticks;
-  bool skipdraw = false;
+  Uint32 last_ticks;
+  Uint32 elapsed_ticks = 0;
+  int skipped_frames = 0;
 
   running = true;
   while(running) {
-    while( (next_screen.get() != NULL || nextpop) &&
-            (screen_fade.get() == NULL || screen_fade->done())) {
-      if(current_screen.get() != NULL) {
-        current_screen->leave();
-      }
 
-      if(nextpop) {
-        if(screen_stack.empty()) {
-          running = false;
-          break;
-        }
-        next_screen.reset(screen_stack.back());
-        screen_stack.pop_back();
-      }
-      if(nextpush && current_screen.get() != NULL) {
-        screen_stack.push_back(current_screen.release());
-      }
+    handle_screen_switch();
+    if(!running || current_screen.get() == NULL)
+      break;
+
+    Uint32 ticks = SDL_GetTicks();
+    elapsed_ticks += ticks - last_ticks;
+    last_ticks = ticks;
 
-      nextpush = false;
-      nextpop = false;
-      speed = 1.0;
-      if(next_screen.get() != NULL)
-        next_screen->setup();
-      current_screen.reset(next_screen.release());
-      screen_fade.reset(NULL);
+    /* time for a new logical frame? */
+    if(elapsed_ticks > TICKS_PER_FRAME) {
+      elapsed_ticks -= TICKS_PER_FRAME;
+      float timestep = 1.0 / LOGICAL_FPS;
+      real_time += timestep;
+      timestep *= speed;
+      game_time += timestep;
 
-      waiting_threads.wakeup();
+      process_events();
+      update_gamelogic(timestep);
     }
 
-    if(!running || current_screen.get() == NULL)
-      break;
+    if(elapsed_ticks > TICKS_PER_FRAME) {
+      // too much time passed since last frame, the computer doesn't seem to be
+      // able to draw 64fps on screen, so skip 1 frame for now
+      skipped_frames++;
 
-    float elapsed_time = 1.0 / LOGICAL_FPS;
-    ticks = SDL_GetTicks();
-    if(ticks > fps_nextframe_ticks) {
-      if(skipdraw) {
-        // already skipped last frame? we have to slow down the game then...
-        skipdraw = false;
-        fps_nextframe_ticks -= (Uint32) (1000.0 / LOGICAL_FPS);
-      } else {
-        // don't draw all frames when we're getting too slow
-        skipdraw = true;
+      // slow down the game if we skip too many frames
+      if(skipped_frames > MAX_FRAME_SKIP) {
+        elapsed_ticks = elapsed_ticks % TICKS_PER_FRAME;
+        draw(context);
+        skipped_frames = 0;
       }
     } else {
-      skipdraw = false;
-      while(fps_nextframe_ticks > ticks) {
-        /* just wait */
-        // If we really have to wait long, then do an imprecise SDL_Delay()
-        Uint32 diff = fps_nextframe_ticks - ticks;
-        if(diff > 10) {
-          SDL_Delay(diff - 2);
-        }
-        ticks = SDL_GetTicks();
-      }
-    }
-    fps_nextframe_ticks = ticks + (Uint32) (1000.0 / LOGICAL_FPS);
-
-    if(!skipdraw) {
-      current_screen->draw(context);
-      if(Menu::current() != NULL)
-        Menu::current()->draw(context);
-      if(screen_fade.get() != NULL)
-        screen_fade->draw(context);
-      Console::instance->draw(context);
-
-      if(config->show_fps)
-        draw_fps(context, fps_fps);
-
-      context.do_drawing();
-
-      /* Calculate frames per second */
-      if(config->show_fps)
-      {
-        ++frame_count;
-
-        if(SDL_GetTicks() - fps_ticks >= 500)
-        {
-          fps_fps = (float) frame_count / .5;
-          frame_count = 0;
-          fps_ticks = SDL_GetTicks();
-        }
-      }
-    }
-
-    real_time += elapsed_time;
-    elapsed_time *= speed;
-    game_time += elapsed_time;
-
-    Scripting::update_debugger();
-    Scripting::TimeScheduler::instance->update(game_time);
-    current_screen->update(elapsed_time);
-    if(screen_fade.get() != NULL)
-      screen_fade->update(elapsed_time);
-    Console::instance->update(elapsed_time);
-
-    main_controller->update();
-    SDL_Event event;
-    while(SDL_PollEvent(&event)) {
-      main_controller->process_event(event);
-      if(Menu::current() != NULL)
-        Menu::current()->event(event);
-      if(event.type == SDL_QUIT)
-        quit();
+      draw(context);
+      skipped_frames = 0;
     }
 
     sound_manager->update();
-
-    //log_info << "== periodic rand() = " << systemRandom.rand() << std::endl;
   }
 }
index a69baaf..cb5bbed 100644 (file)
@@ -48,11 +48,17 @@ public:
 
 private:
   void draw_fps(DrawingContext& context, float fps);
+  void draw(DrawingContext& context);
+  void update_gamelogic(float elapsed_time);
+  void process_events();
+  void handle_screen_switch();
 
   bool running;
   float speed;
   bool nextpop;
   bool nextpush;
+  /// measured fps
+  float fps;
   std::auto_ptr<Screen> next_screen;
   std::auto_ptr<Screen> current_screen;
   std::auto_ptr<Console> console;
index c3f71d1..0ee8774 100644 (file)
@@ -989,7 +989,7 @@ WorldMap::load_state()
     // get table for our world
     sq_pushstring(vm, map_filename.c_str(), map_filename.length());
     if(SQ_FAILED(sq_get(vm, -2)))
-      throw Scripting::SquirrelError(vm, "Couldn't get state.world.mapfilename");
+      throw Scripting::SquirrelError(vm, "Couldn't get state.worlds.mapfilename");
 
     // load tux
     sq_pushstring(vm, "tux", -1);