[gens-sdl] Moved the frameskip code into EmuLoop::runFrame().
authorDavid Korth <gerbilsoft@gerbilsoft.com>
Sun, 6 Sep 2015 16:31:37 +0000 (12:31 -0400)
committerDavid Korth <gerbilsoft@gerbilsoft.com>
Sun, 6 Sep 2015 16:31:37 +0000 (12:31 -0400)
EmuLoop::runFrame() calls abstract virtual functions runFullFrame() and
runFastFrame() depending on the number of frames to skip. These are
implemented by the subclasses.

This eliminates the frameskip duplication between EmuLoop and CrazyEffectLoop.

src/gens-sdl/CrazyEffectLoop.cpp
src/gens-sdl/CrazyEffectLoop.hpp
src/gens-sdl/EmuLoop.cpp
src/gens-sdl/EmuLoop.hpp
src/gens-sdl/EventLoop.cpp
src/gens-sdl/EventLoop.hpp
src/gens-sdl/EventLoop_p.hpp

index 3232dce..c905878 100644 (file)
@@ -115,9 +115,12 @@ int CrazyEffectLoop::run(const char *rom_filename)
        // Check for startup messages.
        checkForStartupMessages();
 
-       // Start the frame timer.
-       // TODO: Region code?
-       d->clks.reset();
+       // Set frame timing.
+       // NOTE: Always running the "Crazy Effect" at 60 fps.
+       // TODO: Maybe 50 to compensate for dropout?
+       d->setFrameTiming(60);
+       // Disable frameskip for the "Crazy Effect".
+       d->frameskip = false;
 
        // Create the "Crazy" Effect framebuffer.
        // Image size defaults to the full framebuffer,
@@ -151,35 +154,9 @@ int CrazyEffectLoop::run(const char *rom_filename)
                        continue;
                }
 
-               // New start time.
-               d->clks.new_clk = d->clks.timing.getTime();
-
-               // Update the FPS counter.
-               unsigned int fps_tmp = ((d->clks.new_clk - d->clks.fps_clk) & 0x3FFFFF);
-               if (fps_tmp >= 1000000) {
-                       // More than 1 second has passed.
-                       d->clks.fps_clk = d->clks.new_clk;
-                       // FIXME: Just use abs() here.
-                       if (d->clks.frames_old > d->clks.frames) {
-                               d->clks.fps = (d->clks.frames_old - d->clks.frames);
-                       } else {
-                               d->clks.fps = (d->clks.frames - d->clks.frames_old);
-                       }
-                       d->clks.frames_old = d->clks.frames;
-
-                       // Update the window title.
-                       // TODO: Average the FPS over multiple seconds
-                       // and/or quarter-seconds.
-                       char win_title[256];
-                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", d->clks.fps);
-                       d->sdlHandler->set_window_title(win_title);
-               }
-
-               // Run the "Crazy" effect.
-               // TODO: Use the frameskip code to limit frames?
-               d->crazyEffect->run(d->crazyFb);
-               d->sdlHandler->update_video();
-               d->clks.frames++;
+               // Run a frame.
+               // EventLoop::runFrame() handles frameskip timing.
+               runFrame();
                yield();
                continue;
        }
@@ -203,4 +180,29 @@ int CrazyEffectLoop::run(const char *rom_filename)
        return 0;
 }
 
+/**
+ * Run a normal frame.
+ * This function is called by runFrame(),
+ * and should be handled by running a full
+ * frame with video and audio updates.
+ */
+void CrazyEffectLoop::runFullFrame(void)
+{
+       CrazyEffectLoopPrivate *const d = d_func();
+       d->crazyEffect->run(d->crazyFb);
+}
+
+/**
+ * Run a fast frame.
+ * This function is called by runFrame() if the
+ * system is lagging a bit, and should be handled
+ * by running a frame with audio updates only.
+ */
+void CrazyEffectLoop::runFastFrame(void)
+{
+       // No fast frames here.
+       // Run the full frame version.
+       runFullFrame();
+}
+
 }
index f17b147..e9b5002 100644 (file)
@@ -48,6 +48,25 @@ class CrazyEffectLoop : public EventLoop
                 * @return Exit code.
                 */
                virtual int run(const char *rom_filename) final;
+
+       protected:
+               // TODO: Move to CrazyEffectLoopPrivate?
+
+               /**
+                * Run a normal frame.
+                * This function is called by runFrame(),
+                * and should be handled by running a full
+                * frame with video and audio updates.
+                */
+               virtual void runFullFrame(void) final;
+
+               /**
+                * Run a fast frame.
+                * This function is called by runFrame() if the
+                * system is lagging a bit, and should be handled
+                * by running a frame with audio updates only.
+                */
+               virtual void runFastFrame(void) final;
 };
 
 }
index 2bf297f..99651c9 100644 (file)
@@ -583,11 +583,10 @@ int EmuLoop::run(const char *rom_filename)
        // Check for startup messages.
        checkForStartupMessages();
 
-       // Start the frame timer.
+       // Set frame timing.
        // TODO: Region code?
        bool isPal = false;
-       const unsigned int usec_per_frame = (1000000 / (isPal ? 50 : 60));
-       d->clks.reset();
+       d->setFrameTiming(isPal ? 50 : 60);
 
        // TODO: Close the ROM, or let EmuContext do it?
 
@@ -647,94 +646,13 @@ int EmuLoop::run(const char *rom_filename)
                        continue;
                }
 
-               // New start time.
-               d->clks.new_clk = d->clks.timing.getTime();
-
-               // Update the FPS counter.
-               unsigned int fps_tmp = ((d->clks.new_clk - d->clks.fps_clk) & 0x3FFFFF);
-               if (fps_tmp >= 1000000) {
-                       // More than 1 second has passed.
-                       d->clks.fps_clk = d->clks.new_clk;
-                       // FIXME: Just use abs() here.
-                       if (d->clks.frames_old > d->clks.frames) {
-                               d->clks.fps = (d->clks.frames_old - d->clks.frames);
-                       } else {
-                               d->clks.fps = (d->clks.frames - d->clks.frames_old);
-                       }
-                       d->clks.frames_old = d->clks.frames;
-
-                       // Update the window title.
-                       // TODO: Average the FPS over multiple seconds
-                       // and/or quarter-seconds.
-                       char win_title[256];
-                       snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", d->clks.fps);
-                       d->sdlHandler->set_window_title(win_title);
-               }
+               // Run a frame.
+               // EventLoop::runFrame() handles frameskip timing.
+               runFrame();
 
-               // Frameskip.
-               if (d->frameskip) {
-                       // Determine how many frames to run.
-                       d->clks.usec_frameskip += ((d->clks.new_clk - d->clks.old_clk) & 0x3FFFFF); // no more than 4 secs
-                       unsigned int frames_todo = (unsigned int)(d->clks.usec_frameskip / usec_per_frame);
-                       d->clks.usec_frameskip %= usec_per_frame;
-                       d->clks.old_clk = d->clks.new_clk;
-
-                       if (frames_todo == 0) {
-                               // No frames to do yet.
-                               // Wait until the next frame.
-                               uint64_t usec_sleep = (usec_per_frame - d->clks.usec_frameskip);
-                               if (usec_sleep > 1000) {
-                                       // Never sleep for longer than the 50 Hz value
-                                       // so events are checked often enough.
-                                       if (usec_sleep > (1000000 / 50)) {
-                                               usec_sleep = (1000000 / 50);
-                                       }
-                                       usec_sleep -= 1000;
-
-#ifdef _WIN32
-                                       // Win32: Use a yield() loop.
-                                       // FIXME: Doesn't work properly on VBox/WinXP...
-                                       uint64_t yield_end = d->clks.timing.getTime() + usec_sleep;
-                                       do {
-                                               yield();
-                                       } while (yield_end > d->clks.timing.getTime());
-#else /* !_WIN32 */
-                                       // Linux: Use usleep().
-                                       usleep(usec_sleep);
-#endif /* _WIN32 */
-                               }
-                       } else {
-                               // Draw frames.
-                               for (; frames_todo != 1; frames_todo--) {
-                                       // Run a frame without rendering.
-                                       d->emuContext->execFrameFast();
-                                       d->sdlHandler->update_audio();
-                               }
-                               frames_todo = 0;
-
-                               // Run a frame and render it.
-                               d->emuContext->execFrame();
-                               d->sdlHandler->update_audio();
-                               d->sdlHandler->update_video();
-                               // Increment the frame counter.
-                               d->clks.frames++;
-
-                               // Autosave SRAM/EEPROM.
-                               // TODO: EmuContext::execFrame() should probably do this itself...
-                               d->emuContext->autoSaveData(1);
-                       }
-               } else {
-                       // Run a frame and render it.
-                       d->emuContext->execFrame();
-                       d->sdlHandler->update_audio();
-                       d->sdlHandler->update_video();
-                       // Increment the frame counter.
-                       d->clks.frames++;
-
-                       // Autosave SRAM/EEPROM.
-                       // TODO: EmuContext::execFrame() should probably do this itself...
-                       d->emuContext->autoSaveData(1);
-               }
+               // Autosave SRAM/EEPROM.
+               // TODO: EmuContext::execFrame() should probably do this itself...
+               d->emuContext->autoSaveData(1);
 
                // Update the I/O manager.
                d->keyManager->updateIoManager(d->emuContext->m_ioManager);
@@ -771,4 +689,28 @@ int EmuLoop::run(const char *rom_filename)
        return 0;
 }
 
+/**
+ * Run a normal frame.
+ * This function is called by runFrame(),
+ * and should be handled by running a full
+ * frame with video and audio updates.
+ */
+void EmuLoop::runFullFrame(void)
+{
+       EmuLoopPrivate *const d = d_func();
+       d->emuContext->execFrame();
+}
+
+/**
+ * Run a fast frame.
+ * This function is called by runFrame() if the
+ * system is lagging a bit, and should be handled
+ * by running a frame with audio updates only.
+ */
+void EmuLoop::runFastFrame(void)
+{
+       EmuLoopPrivate *const d = d_func();
+       d->emuContext->execFrameFast();
+}
+
 }
index 919e860..58e45cf 100644 (file)
@@ -61,6 +61,25 @@ class EmuLoop : public EventLoop
                 * @return 0 if the event was handled; non-zero if it wasn't.
                 */
                virtual int processSdlEvent(const SDL_Event *event) final;
+
+       protected:
+               // TODO: Move to EmuLoopPrivate?
+
+               /**
+                * Run a normal frame.
+                * This function is called by runFrame(),
+                * and should be handled by running a full
+                * frame with video and audio updates.
+                */
+               virtual void runFullFrame(void) final;
+
+               /**
+                * Run a fast frame.
+                * This function is called by runFrame() if the
+                * system is lagging a bit, and should be handled
+                * by running a frame with audio updates only.
+                */
+               virtual void runFastFrame(void) final;
 };
 
 }
index a102045..01fb059 100644 (file)
@@ -57,8 +57,12 @@ EventLoopPrivate::EventLoopPrivate()
        , autoPause(false)
        , exposed(false)
        , lastF1time(0)
+       , usec_per_frame(0)
 {
        paused.data = 0;
+
+       // Default to 60 fps.
+       setFrameTiming(60);
 }
 
 EventLoopPrivate::~EventLoopPrivate()
@@ -163,6 +167,17 @@ void EventLoopPrivate::doAboutMessage(void)
        lastF1time = curTime;
 }
 
+/**
+ * Set frame timing.
+ * This resets the frameskip timers.
+ * @param framerate Frame rate, e.g. 50 or 60.
+ */
+void EventLoopPrivate::setFrameTiming(int framerate)
+{
+       usec_per_frame = (1000000 / framerate);
+       clks.reset();
+}
+
 /** EventLoop **/
 
 EventLoop::EventLoop(EventLoopPrivate *d)
@@ -342,4 +357,93 @@ void EventLoop::processSdlEventQueue(void)
        d_ptr->exposed = false;
 }
 
+/**
+ * Run a frame.
+ * This function handles frameskip timing.
+ * Call this function from run().
+ */
+void EventLoop::runFrame(void)
+{
+       // New start time.
+       d_ptr->clks.new_clk = d_ptr->clks.timing.getTime();
+
+       // Update the FPS counter.
+       unsigned int fps_tmp = ((d_ptr->clks.new_clk - d_ptr->clks.fps_clk) & 0x3FFFFF);
+       if (fps_tmp >= 1000000) {
+               // More than 1 second has passed.
+               d_ptr->clks.fps_clk = d_ptr->clks.new_clk;
+               // FIXME: Just use abs() here.
+               if (d_ptr->clks.frames_old > d_ptr->clks.frames) {
+                       d_ptr->clks.fps = (d_ptr->clks.frames_old - d_ptr->clks.frames);
+               } else {
+                       d_ptr->clks.fps = (d_ptr->clks.frames - d_ptr->clks.frames_old);
+               }
+               d_ptr->clks.frames_old = d_ptr->clks.frames;
+
+               // Update the window title.
+               // TODO: Average the FPS over multiple seconds
+               // and/or quarter-seconds.
+               char win_title[256];
+               snprintf(win_title, sizeof(win_title), "Gens/GS II [SDL] - %u fps", d_ptr->clks.fps);
+               d_ptr->sdlHandler->set_window_title(win_title);
+       }
+
+       // Frameskip.
+       if (d_ptr->frameskip) {
+               // Determine how many frames to run.
+               d_ptr->clks.usec_frameskip += ((d_ptr->clks.new_clk - d_ptr->clks.old_clk) & 0x3FFFFF); // no more than 4 secs
+               unsigned int frames_todo = (unsigned int)(d_ptr->clks.usec_frameskip / d_ptr->usec_per_frame);
+               d_ptr->clks.usec_frameskip %= d_ptr->usec_per_frame;
+               d_ptr->clks.old_clk = d_ptr->clks.new_clk;
+
+               if (frames_todo == 0) {
+                       // No frames to do yet.
+                       // Wait until the next frame.
+                       uint64_t usec_sleep = (d_ptr->usec_per_frame - d_ptr->clks.usec_frameskip);
+                       if (usec_sleep > 1000) {
+                               // Never sleep for longer than the 50 Hz value
+                               // so events are checked often enough.
+                               if (usec_sleep > (1000000 / 50)) {
+                                       usec_sleep = (1000000 / 50);
+                               }
+                               usec_sleep -= 1000;
+
+#ifdef _WIN32
+                               // Win32: Use a yield() loop.
+                               // FIXME: Doesn't work properly on VBox/WinXP...
+                               uint64_t yield_end = d_ptr->clks.timing.getTime() + usec_sleep;
+                               do {
+                                       yield();
+                               } while (yield_end > d_ptr->clks.timing.getTime());
+#else /* !_WIN32 */
+                               // Linux: Use usleep().
+                               usleep(usec_sleep);
+#endif /* _WIN32 */
+                       }
+               } else {
+                       // Draw frames.
+                       for (; frames_todo != 1; frames_todo--) {
+                               // Run a frame without rendering.
+                               runFastFrame();
+                               d_ptr->sdlHandler->update_audio();
+                       }
+                       frames_todo = 0;
+
+                       // Run a frame and render it.
+                       runFullFrame();
+                       d_ptr->sdlHandler->update_audio();
+                       d_ptr->sdlHandler->update_video();
+                       // Increment the frame counter.
+                       d_ptr->clks.frames++;
+               }
+       } else {
+               // Run a frame and render it.
+               runFullFrame();
+               d_ptr->sdlHandler->update_audio();
+               d_ptr->sdlHandler->update_video();
+               // Increment the frame counter.
+               d_ptr->clks.frames++;
+       }
+}
+
 }
index 2935f32..896c0ef 100644 (file)
@@ -81,6 +81,32 @@ class EventLoop
                 * to wait for the next event.
                 */
                void processSdlEventQueue(void);
+
+       protected:
+               // TODO: Move to EventLoopPrivate?
+
+               /**
+                * Run a frame.
+                * This function handles frameskip timing.
+                * Call this function from run().
+                */
+               void runFrame(void);
+
+               /**
+                * Run a normal frame.
+                * This function is called by runFrame(),
+                * and should be handled by running a full
+                * frame with video and audio updates.
+                */
+               virtual void runFullFrame(void) = 0;
+
+               /**
+                * Run a fast frame.
+                * This function is called by runFrame() if the
+                * system is lagging a bit, and should be handled
+                * by running a frame with audio updates only.
+                */
+               virtual void runFastFrame(void) = 0;
 };
 
 }
index 6a91801..285ac46 100644 (file)
@@ -125,6 +125,7 @@ class EventLoopPrivate
                        public:
                                // Reset frameskip timers.
                                void reset(void) {
+                                       // TODO: Reset timing's base?
                                        start_clk = timing.getTime();
                                        old_clk = start_clk;
                                        fps_clk = start_clk;
@@ -160,6 +161,16 @@ class EventLoopPrivate
                // This is here to prevent the user from spamming
                // the display with the message.
                uint64_t lastF1time;
+
+               // Microseconds per frame.
+               unsigned int usec_per_frame;
+
+               /**
+                * Set frame timing.
+                * This resets the frameskip timers.
+                * @param framerate Frame rate, e.g. 50 or 60.
+                */
+               void setFrameTiming(int framerate);
 };
 
 }