Permalink
Browse files

GUI: Make autosave portable

  • Loading branch information...
endrift committed Jan 20, 2018
1 parent 0e9ba00 commit 45c2fdf7ed224b11b4175d816451e645e4a45245
Showing with 125 additions and 103 deletions.
  1. +97 −30 src/feature/gui/gui-runner.c
  2. +20 −0 src/feature/gui/gui-runner.h
  3. +8 −73 src/platform/3ds/main.c
@@ -23,6 +23,7 @@
mLOG_DECLARE_CATEGORY(GUI_RUNNER);
mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner", "gui.runner");
+#define AUTOSAVE_GRANULARITY 600
#define FPS_GRANULARITY 120
#define FPS_BUFFER_SIZE 3
@@ -34,19 +35,11 @@ enum {
RUNNER_SCREENSHOT,
RUNNER_CONFIG,
RUNNER_RESET,
- RUNNER_COMMAND_MASK = 0xFFFF,
-
- RUNNER_STATE_1 = 0x10000,
- RUNNER_STATE_2 = 0x20000,
- RUNNER_STATE_3 = 0x30000,
- RUNNER_STATE_4 = 0x40000,
- RUNNER_STATE_5 = 0x50000,
- RUNNER_STATE_6 = 0x60000,
- RUNNER_STATE_7 = 0x70000,
- RUNNER_STATE_8 = 0x80000,
- RUNNER_STATE_9 = 0x90000,
+ RUNNER_COMMAND_MASK = 0xFFFF
};
+#define RUNNER_STATE(X) ((X) << 16)
+
static const struct mInputPlatformInfo _mGUIKeyInfo = {
.platformName = "gui",
.keyId = (const char*[GUI_INPUT_MAX]) {
@@ -141,6 +134,21 @@ static uint8_t _readLux(struct GBALuminanceSource* lux) {
return 0xFF - value;
}
+static void _tryAutosave(struct mGUIRunner* runner) {
+#ifdef DISABLE_THREADING
+ mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
+#else
+ if (!runner->autosave.buffer) {
+ runner->autosave.buffer = VFileMemChunk(NULL, 0);
+ }
+ MutexLock(&runner->autosave.mutex);
+ runner->autosave.core = runner->core;
+ mCoreSaveStateNamed(runner->core, runner->autosave.buffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
+ ConditionWake(&runner->autosave.cond);
+ MutexUnlock(&runner->autosave.mutex);
+#endif
+}
+
void mGUIInit(struct mGUIRunner* runner, const char* port) {
GUIInit(&runner->params);
runner->port = port;
@@ -174,9 +182,34 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) {
strncpy(runner->params.currentPath, lastPath, PATH_MAX - 1);
runner->params.currentPath[PATH_MAX - 1] = '\0';
}
+
+#ifndef DISABLE_THREADING
+ if (!runner->autosave.running) {
+ runner->autosave.running = true;
+ MutexInit(&runner->autosave.mutex);
+ ConditionInit(&runner->autosave.cond);
+ ThreadCreate(&runner->autosave.thread, mGUIAutosaveThread, &runner->autosave);
+ }
+#endif
}
void mGUIDeinit(struct mGUIRunner* runner) {
+#ifndef DISABLE_THREADING
+ MutexLock(&runner->autosave.mutex);
+ runner->autosave.running = false;
+ ConditionWake(&runner->autosave.cond);
+ MutexUnlock(&runner->autosave.mutex);
+
+ ThreadJoin(runner->autosave.thread);
+
+ ConditionDeinit(&runner->autosave.cond);
+ MutexDeinit(&runner->autosave.mutex);
+
+ if (runner->autosave.buffer) {
+ runner->autosave.buffer->close(runner->autosave.buffer);
+ }
+#endif
+
if (runner->teardown) {
runner->teardown(runner);
}
@@ -239,25 +272,26 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_1) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_2) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_3) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_4) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_5) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_6) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_7) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_8) };
- *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_9) };
-
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_1) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_2) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_3) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_4) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_5) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_6) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_7) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_8) };
- *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_9) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(1)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(2)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(3)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(4)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(5)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(6)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(7)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(8)) };
+ *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(9)) };
+
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "Autosave", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(0)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(1)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(2)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(3)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(4)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(5)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(6)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(7)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(8)) };
+ *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT };
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG };
@@ -325,6 +359,13 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
}
bool running = true;
+
+#ifndef DISABLE_THREADING
+ MutexLock(&runner->autosave.mutex);
+ runner->autosave.core = runner->core;
+ MutexUnlock(&runner->autosave.mutex);
+#endif
+
if (runner->gameLoaded) {
runner->gameLoaded(runner);
}
@@ -415,6 +456,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->fps = (CircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t));
}
}
+ if (runner->core->frameCounter(runner->core) % AUTOSAVE_GRANULARITY == 0) {
+ _tryAutosave(runner);
+ }
}
}
@@ -467,6 +511,11 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
if (runner->gameUnloaded) {
runner->gameUnloaded(runner);
}
+#ifndef DISABLE_THREADING
+ MutexLock(&runner->autosave.mutex);
+ runner->autosave.core = NULL;
+ MutexUnlock(&runner->autosave.mutex);
+#endif
mLOG(GUI_RUNNER, DEBUG, "Unloading game...");
runner->core->unloadROM(runner->core);
drawState.screenshotId = 0;
@@ -524,3 +573,21 @@ void mGUIRunloop(struct mGUIRunner* runner) {
mGUIRun(runner, path);
}
}
+
+#ifndef DISABLE_THREADING
+THREAD_ENTRY mGUIAutosaveThread(void* context) {
+ struct mGUIAutosaveContext* autosave = context;
+ MutexLock(&autosave->mutex);
+ while (autosave->running) {
+ ConditionWait(&autosave->cond, &autosave->mutex);
+ if (autosave->running && autosave->core) {
+ struct VFile* vf = mCoreGetState(autosave->core, 0, true);
+ void* mem = autosave->buffer->map(autosave->buffer, autosave->buffer->size(autosave->buffer), MAP_READ);
+ vf->write(vf, mem, autosave->buffer->size(autosave->buffer));
+ autosave->buffer->unmap(autosave->buffer, mem, autosave->buffer->size(autosave->buffer));
+ vf->close(vf);
+ }
+ }
+ MutexUnlock(&autosave->mutex);
+}
+#endif
@@ -15,6 +15,7 @@ CXX_GUARD_START
#include <mgba/internal/gba/hardware.h>
#include <mgba-util/circle-buffer.h>
#include <mgba-util/gui.h>
+#include <mgba-util/threading.h>
enum mGUIInput {
mGUI_INPUT_INCREASE_BRIGHTNESS = GUI_INPUT_USER_START,
@@ -38,12 +39,27 @@ struct mGUIRunnerLux {
int luxLevel;
};
+#ifndef DISABLE_THREADING
+struct VFile;
+struct mGUIAutosaveContext {
+ struct VFile* buffer;
+ struct mCore* core;
+ Thread thread;
+ Mutex mutex;
+ Condition cond;
+ bool running;
+};
+#endif
+
struct mGUIRunner {
struct mCore* core;
struct GUIParams params;
struct mGUIBackground background;
struct mGUIRunnerLux luminanceSource;
+#ifndef DISABLE_THREADING
+ struct mGUIAutosaveContext autosave;
+#endif
struct mInputMap guiKeys;
struct mCoreConfig config;
@@ -78,6 +94,10 @@ void mGUIDeinit(struct mGUIRunner*);
void mGUIRun(struct mGUIRunner*, const char* path);
void mGUIRunloop(struct mGUIRunner*);
+#ifndef DISABLE_THREADING
+THREAD_ENTRY mGUIAutosaveThread(void* context);
+#endif
+
CXX_GUARD_END
#endif
View
@@ -106,12 +106,6 @@ static C3D_Tex upscaleBufferTex;
static aptHookCookie cookie;
-static Thread autosave;
-static struct VFile* autosaveBuffer = NULL;
-static Mutex autosaveMutex;
-static Condition autosaveCond;
-static struct mCore* autosaveCore = NULL;
-
extern bool allocateRomBuffer(void);
static bool _initGpu(void) {
@@ -201,33 +195,6 @@ static void _aptHook(APT_HookType hook, void* user) {
}
}
-static void _autosaveThread(void* context) {
- bool* running = context;
- MutexLock(&autosaveMutex);
- while (*running) {
- ConditionWait(&autosaveCond, &autosaveMutex);
- if (*running && autosaveCore) {
- struct VFile* vf = mCoreGetState(autosaveCore, 0, true);
- void* mem = autosaveBuffer->map(autosaveBuffer, autosaveBuffer->size(autosaveBuffer), MAP_READ);
- vf->write(vf, mem, autosaveBuffer->size(autosaveBuffer));
- autosaveBuffer->unmap(autosaveBuffer, mem, autosaveBuffer->size(autosaveBuffer));
- vf->close(vf);
- }
- }
- MutexUnlock(&autosaveMutex);
-}
-
-static void _tryAutosave(struct mCore* core) {
- if (!autosaveBuffer) {
- autosaveBuffer = VFileMemChunk(NULL, 0);
- }
- MutexLock(&autosaveMutex);
- autosaveCore = core;
- mCoreSaveStateNamed(core, autosaveBuffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
- ConditionWake(&autosaveCond);
- MutexUnlock(&autosaveMutex);
-}
-
static void _map3DSKey(struct mInputMap* map, int ctrKey, enum GBAKey key) {
mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key);
}
@@ -460,10 +427,6 @@ static void _gameLoaded(struct mGUIRunner* runner) {
}
}
}
-
- MutexLock(&autosaveMutex);
- autosaveCore = runner->core;
- MutexUnlock(&autosaveMutex);
}
static void _gameUnloaded(struct mGUIRunner* runner) {
@@ -497,10 +460,6 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
default:
break;
}
-
- MutexLock(&autosaveMutex);
- autosaveCore = NULL;
- MutexUnlock(&autosaveMutex);
}
static void _drawTex(struct mCore* core, bool faded) {
@@ -722,16 +681,7 @@ static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
}
static bool _running(struct mGUIRunner* runner) {
- static int frame = 0;
- if (autosaveCore) {
- ++frame;
- if (frame == 300) {
- _tryAutosave(autosaveCore);
- frame = 0;
- }
- } else {
- frame = 0;
- }
+ UNUSED(runner);
return aptMainLoop();
}
@@ -1081,6 +1031,13 @@ int main() {
.running = _running
};
+ runner.autosave.running = true;
+ MutexInit(&runner.autosave.mutex);
+ ConditionInit(&runner.autosave.cond);
+
+ APT_SetAppCpuTimeLimit(20);
+ runner.autosave.thread = threadCreate(mGUIAutosaveThread, &runner.autosave, 0x4000, 0x1F, 1, true);
+
mGUIInit(&runner, "3ds");
_map3DSKey(&runner.params.keyMap, KEY_X, GUI_INPUT_CANCEL);
@@ -1094,29 +1051,7 @@ int main() {
_map3DSKey(&runner.params.keyMap, KEY_CSTICK_UP, mGUI_INPUT_INCREASE_BRIGHTNESS);
_map3DSKey(&runner.params.keyMap, KEY_CSTICK_DOWN, mGUI_INPUT_DECREASE_BRIGHTNESS);
- bool autosaveActive = true;
- MutexInit(&autosaveMutex);
- ConditionInit(&autosaveCond);
-
- APT_SetAppCpuTimeLimit(20);
- autosave = threadCreate(_autosaveThread, &autosaveActive, 0x4000, 0x1F, 1, true);
-
mGUIRunloop(&runner);
-
- MutexLock(&autosaveMutex);
- autosaveActive = false;
- ConditionWake(&autosaveCond);
- MutexUnlock(&autosaveMutex);
-
- threadJoin(autosave, U64_MAX);
-
- if (autosaveBuffer) {
- autosaveBuffer->close(autosaveBuffer);
- }
-
- ConditionDeinit(&autosaveCond);
- MutexDeinit(&autosaveMutex);
-
mGUIDeinit(&runner);
_cleanup();

0 comments on commit 45c2fdf

Please sign in to comment.