diff --git a/src/gba/context/context.c b/src/gba/context/context.c index 18f70094c..3aecf2a55 100644 --- a/src/gba/context/context.c +++ b/src/gba/context/context.c @@ -60,7 +60,7 @@ bool GBAContextInit(struct GBAContext* context, const char* port) { GBAConfigLoadDefaults(&context->config, &opts); } - context->gba->sync = 0; + context->gba->sync = &context->sync; return true; } @@ -191,6 +191,11 @@ void GBAContextFrame(struct GBAContext* context, uint16_t keys) { while (frameCounter == context->gba->video.frameCounter) { ARMRunLoop(context->cpu); } + if (context->sync.videoFrameSkip < 0) { + int frameskip = 0; + GBAConfigGetIntValue(&context->config, "frameskip", &frameskip); + context->sync.videoFrameSkip = frameskip; + } } static void _GBAContextLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) { diff --git a/src/gba/context/context.h b/src/gba/context/context.h index fd86dca7c..1fcde3f24 100644 --- a/src/gba/context/context.h +++ b/src/gba/context/context.h @@ -9,6 +9,7 @@ #include "util/common.h" #include "gba/context/config.h" +#include "gba/context/sync.h" #include "gba/input.h" struct GBAContext { @@ -23,6 +24,7 @@ struct GBAContext { struct GBAConfig config; struct GBAOptions opts; struct GBAInputMap inputMap; + struct GBASync sync; }; bool GBAContextInit(struct GBAContext* context, const char* port); diff --git a/src/gba/gui/gui-config.c b/src/gba/gui/gui-config.c index c3d6f5ad3..67380fe38 100644 --- a/src/gba/gui/gui-config.c +++ b/src/gba/gui/gui-config.c @@ -16,6 +16,24 @@ void GBAGUIShowConfig(struct GBAGUIRunner* runner, struct GUIMenuItem* extra, si .background = &runner->background.d }; GUIMenuItemListInit(&menu.items, 0); + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Frameskip", + .data = "frameskip", + .submenu = 0, + .state = 0, + .validStates = (const char*[]) { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 0 + } + }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Show framerate", + .data = "fpsCounter", + .submenu = 0, + .state = false, + .validStates = (const char*[]) { + "Off", "On", 0 + } + }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Use BIOS if found", .data = "useBios", diff --git a/src/gba/gui/gui-runner.c b/src/gba/gui/gui-runner.c index 57330d781..a0751d41c 100644 --- a/src/gba/gui/gui-runner.c +++ b/src/gba/gui/gui-runner.c @@ -108,6 +108,10 @@ void GBAGUIInit(struct GBAGUIRunner* runner, const char* port) { runner->context.gba->luminanceSource = &runner->luminanceSource.d; runner->background.d.draw = _drawBackground; runner->background.p = runner; + runner->fps = 0; + runner->lastFpsCheck = 0; + runner->totalDelta = 0; + CircleBufferInit(&runner->fpsBuffer, FPS_BUFFER_SIZE * sizeof(uint32_t)); if (runner->setup) { runner->setup(runner); } @@ -120,6 +124,7 @@ void GBAGUIDeinit(struct GBAGUIRunner* runner) { if (runner->context.config.port) { GBAConfigSave(&runner->context.config); } + CircleBufferDeinit(&runner->fpsBuffer); GBAContextDeinit(&runner->context); } @@ -212,8 +217,16 @@ void GBAGUIRunloop(struct GBAGUIRunner* runner) { if (runner->gameLoaded) { runner->gameLoaded(runner); } + bool running = true; while (running) { + CircleBufferClear(&runner->fpsBuffer); + runner->totalDelta = 0; + runner->fps = 0; + struct timeval tv; + gettimeofday(&tv, 0); + runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec; + while (true) { uint32_t guiKeys; GUIPollInput(&runner->params, &guiKeys, 0); @@ -239,9 +252,37 @@ void GBAGUIRunloop(struct GBAGUIRunner* runner) { } GBAContextFrame(&runner->context, keys); if (runner->drawFrame) { + int drawFps = false; + GBAConfigGetIntValue(&runner->context.config, "fpsCounter", &drawFps); + runner->params.drawStart(); runner->drawFrame(runner, false); + if (drawFps) { + GUIFontPrintf(runner->params.font, 0, GUIFontHeight(runner->params.font), GUI_TEXT_LEFT, 0x7FFFFFFF, "%.2f fps", runner->fps); + } runner->params.drawEnd(); + + if (runner->context.gba->video.frameCounter % FPS_GRANULARITY == 0) { + if (drawFps) { + struct timeval tv; + gettimeofday(&tv, 0); + uint64_t t = 1000000LL * tv.tv_sec + tv.tv_usec; + uint64_t delta = t - runner->lastFpsCheck; + runner->lastFpsCheck = t; + if (delta > 0x7FFFFFFFLL) { + CircleBufferClear(&runner->fpsBuffer); + runner->fps = 0; + } + if (CircleBufferSize(&runner->fpsBuffer) == CircleBufferCapacity(&runner->fpsBuffer)) { + int32_t last; + CircleBufferRead32(&runner->fpsBuffer, &last); + runner->totalDelta -= last; + } + CircleBufferWrite32(&runner->fpsBuffer, delta); + runner->totalDelta += delta; + runner->fps = (CircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t)); + } + } } } diff --git a/src/gba/gui/gui-runner.h b/src/gba/gui/gui-runner.h index 0ca400940..df6bbca45 100644 --- a/src/gba/gui/gui-runner.h +++ b/src/gba/gui/gui-runner.h @@ -7,6 +7,7 @@ #define GUI_RUNNER_H #include "gba/context/context.h" +#include "util/circle-buffer.h" #include "util/gui.h" enum GBAGUIInput { @@ -38,6 +39,11 @@ struct GBAGUIRunner { struct GUIMenuItem* configExtra; size_t nConfigExtra; + float fps; + int64_t lastFpsCheck; + int32_t totalDelta; + struct CircleBuffer fpsBuffer; + void (*setup)(struct GBAGUIRunner*); void (*teardown)(struct GBAGUIRunner*); void (*gameLoaded)(struct GBAGUIRunner*);